Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## 2026-04-13 - [Memoize Custom IconifyIcon Component]
**Learning:** The custom `IconifyIcon` component () manually iterates over 11 imported icon sets for prefix-less icons using `getIconData` on every single render. This is extremely expensive given how often icons are rendered in the layout.
**Action:** Implemented a simple in-memory cache dictionary (`iconCache`) outside the component to store positive AND negative lookup results, reducing an (N)$ lookup to (1)$ after the first render.
## 2026-04-13 - [Memoize Custom IconifyIcon Component]
**Learning:** The custom IconifyIcon component manually iterates over 11 imported icon sets for prefix-less icons using getIconData on every single render. This is extremely expensive given how often icons are rendered in the layout.
**Action:** Implemented a simple in-memory cache dictionary (iconCache) outside the component to store positive AND negative lookup results, reducing an O(N) lookup to O(1) after the first render.
19 changes: 17 additions & 2 deletions client/src/components/base/IconifyIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,33 @@ const iconSets: Record<string, IconifyJSON> = {
"mdi-light": mdiLightIcons,
};

// ⚑ Bolt Optimization: Cache icon data lookups to prevent redundant O(N) searches across 11 icon sets on every render.
const iconCache: Record<string, ReturnType<typeof getIconData> | null> = {};

const iconData = (icon: string) => {
if (iconCache[icon] !== undefined) return iconCache[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[icon] = data;
return data;
}
}

for (const [_, icons] of Object.entries(iconSets)) {
const data = getIconData(icons, name);
if (data) return data;
if (data) {
iconCache[icon] = data;
return data;
}
}

// Cache negative results too so we don't repeat the loop for missing icons
iconCache[icon] = null;
return null;
};

const IconifyIcon = ({
Expand Down