From 5663064b46d0d61d02c5483112e57177d7f15d02 Mon Sep 17 00:00:00 2001 From: Brett Jephson Date: Thu, 30 Apr 2026 17:30:38 +0100 Subject: [PATCH] Speed up icon rendering --- packages/icons/src/Icon.tsx | 7 +++++-- packages/icons/src/symbols.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/icons/src/Icon.tsx b/packages/icons/src/Icon.tsx index 402c40401..7db56110c 100644 --- a/packages/icons/src/Icon.tsx +++ b/packages/icons/src/Icon.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { IconSymbolLoader } from './IconSymbolLoader'; import { getIconAssetURL, useIcons } from './IconsProvider'; import { getIconStyle } from './getIconStyle'; -import { getIconSymbolId, registerServerIconSymbol } from './symbols'; +import { getIconSymbolId, prefetchServerIconAsset, registerServerIconSymbol } from './symbols'; import type { IconName, IconStyle } from './types'; /** @@ -55,12 +55,15 @@ export const Icon = React.forwardRef(function Icon( const maskId = React.useId(); const iconInstanceId = React.useId(); const symbolId = getIconSymbolId(iconStyle, icon); + const iconAssetURL = getIconAssetURL(context, iconStyle, icon); if (context.renderMode === 'symbol') { + prefetchServerIconAsset(iconAssetURL); registerServerIconSymbol({ style: iconStyle, icon, symbolId, + assetURL: iconAssetURL, }); return ( @@ -111,7 +114,7 @@ export const Icon = React.forwardRef(function Icon( > { return store[REGISTERED_SYMBOLS_KEY]; } +function getPrefetchedAssetsStore(): Map> { + const store = globalThis as typeof globalThis & { + [PREFETCHED_ICON_ASSETS_KEY]?: Map>; + }; + + if (!store[PREFETCHED_ICON_ASSETS_KEY]) { + store[PREFETCHED_ICON_ASSETS_KEY] = new Map>(); + } + + return store[PREFETCHED_ICON_ASSETS_KEY]; +} + function shouldTrackSymbolRegistrations() { const runtime = globalThis as typeof globalThis & { Bun?: unknown }; @@ -48,6 +62,27 @@ export function registerServerIconSymbol(symbol: RegisteredIconSymbol): void { getRegisteredSymbolsStore().set(`${symbol.style}/${symbol.icon}`, symbol); } +/** + * Start fetching a raw SVG asset during SSR so sprite generation can reuse the in-flight or warm + * request instead of waiting until the end of the render. + */ +export function prefetchServerIconAsset(assetURL: string): void { + if (typeof window !== 'undefined') { + return; + } + + const prefetchedAssets = getPrefetchedAssetsStore(); + if (prefetchedAssets.has(assetURL)) { + return; + } + + const request = fetch(assetURL, { cache: 'force-cache' }) + .then(() => undefined) + .catch(() => undefined); + + prefetchedAssets.set(assetURL, request); +} + /** * Return the currently registered server-rendered symbols in insertion order. */