Speed up icon rendering

This commit is contained in:
Brett Jephson
2026-04-30 17:30:38 +01:00
parent 6246b106af
commit 5663064b46
2 changed files with 40 additions and 2 deletions
+5 -2
View File
@@ -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(
>
<image
data-testid="mask-image"
href={getIconAssetURL(context, iconStyle, icon)}
href={iconAssetURL}
width="100%"
height="100%"
preserveAspectRatio="xMidYMid meet"
+35
View File
@@ -4,9 +4,11 @@ export interface RegisteredIconSymbol {
style: string;
icon: string;
symbolId: string;
assetURL?: string;
}
const REGISTERED_SYMBOLS_KEY = Symbol.for('gitbook.icons.registeredSymbols');
const PREFETCHED_ICON_ASSETS_KEY = Symbol.for('gitbook.icons.prefetchedAssets');
function sanitizeSymbolFragment(fragment: string): string {
return fragment.replace(/[^a-zA-Z0-9_-]/g, '-');
@@ -31,6 +33,18 @@ function getRegisteredSymbolsStore(): Map<string, RegisteredIconSymbol> {
return store[REGISTERED_SYMBOLS_KEY];
}
function getPrefetchedAssetsStore(): Map<string, Promise<void>> {
const store = globalThis as typeof globalThis & {
[PREFETCHED_ICON_ASSETS_KEY]?: Map<string, Promise<void>>;
};
if (!store[PREFETCHED_ICON_ASSETS_KEY]) {
store[PREFETCHED_ICON_ASSETS_KEY] = new Map<string, Promise<void>>();
}
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.
*/