From 775e41790f937d514c38c00b1b72a654b181018f Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 10:52:36 +0000 Subject: [PATCH] perf: Cache IconifyIcon lookups to prevent O(N) iteration Co-authored-by: sshahriazz <34005640+sshahriazz@users.noreply.github.com> --- .jules/bolt.md | 3 +++ client/src/components/base/IconifyIcon.tsx | 21 +++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..8ead340 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2025-02-12 - [IconifyIcon Lookups Unnecessarily Slow] +**Learning:** The custom `IconifyIcon` component iterated through all `iconSets` arrays looking for an icon matches on every render, resulting in O(N) operations. While fast in isolation, rendering hundreds of icons per page compounded this issue drastically slowing down page rendering. +**Action:** Always verify frequently invoked render functions don't iterate arrays to look for exact matches. We implemented a memory cache in `IconifyIcon` to store positive and negative results to convert lookup from O(N) to O(1) saving CPU cycles and speeding up rendering. diff --git a/client/src/components/base/IconifyIcon.tsx b/client/src/components/base/IconifyIcon.tsx index b48f0dc..c2d4928 100644 --- a/client/src/components/base/IconifyIcon.tsx +++ b/client/src/components/base/IconifyIcon.tsx @@ -33,18 +33,35 @@ const iconSets: Record = { "mdi-light": mdiLightIcons, }; +// Cache icon lookups to prevent O(N) array iteration on every render +const iconCache = new Map(); + const iconData = (icon: string) => { + if (iconCache.has(icon)) { + return iconCache.get(icon); + } + const [prefix, name] = icon.includes(":") ? icon.split(":") : ["", icon]; if (prefix && iconSets[prefix]) { const data = getIconData(iconSets[prefix], name); - if (data) return data; + if (data) { + iconCache.set(icon, data); + return data; + } } for (const [_, icons] of Object.entries(iconSets)) { const data = getIconData(icons, name); - if (data) return data; + if (data) { + iconCache.set(icon, data); + return data; + } } + + // Cache negative results as null to satisfy types and avoid repeated misses + iconCache.set(icon, null); + return null; }; const IconifyIcon = ({