diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..4779755 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2026-06-22 - Cached IconifyIcon Resolution +**Learning:** The custom `IconifyIcon` component iterating over an array of 10+ object entries to resolve icon data caused a significant performance penalty per icon rendered. +**Action:** Implemented a `Map` to cache positive and negative lookups. Caching negative results as `null` instead of `undefined` satisfies Next.js's serialization constraints during build and SSR, preventing build errors. diff --git a/client/src/components/base/IconifyIcon.tsx b/client/src/components/base/IconifyIcon.tsx index b48f0dc..c7bab43 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 for icon data to prevent performance-blocking O(N) array iteration lookups +const iconCache = new Map | null>(); + 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 missing icons as null to prevent repeated lookups and satisfy type constraints + iconCache.set(icon, null); + return null; }; const IconifyIcon = ({