Clawscale

Loading icons

Icons named by a string, such as icon="database", need their SVG paths at runtime. Choose how the paths load, above all when you render on the server.

Default loading#

Blueprint loads icon paths asynchronously. The first 16px icon on a page loads the paths of every 16px icon, and the first 20px icon loads the 20px set.

On the server, no paths have loaded. Server-rendered HTML holds an empty placeholder for each icon, which shows a glyph from Blueprint's icon font once that font downloads. After hydration, the SVG replaces it.

<!-- Default: a placeholder -->
<span aria-hidden="true" class="bp6-icon bp6-icon-standard bp6-icon-database" data-icon="database"></span>

<!-- With sync icons: the SVG -->
<span aria-hidden="true" class="bp6-icon bp6-icon-database">
  <svg data-icon="database" height="16" role="img" viewBox="0 0 16 16" width="16"><path d="..."></path></svg>
</span>

Sync icons#

Import @clawscale/react/sync-icons once, in a client module that wraps your app. Every string icon then renders its SVG on the server and in the first client render. It must be a client module, so the import also runs in the browser.

// app/providers.tsx
"use client";

import "@clawscale/react/sync-icons";
import { ClawscaleProvider } from "@clawscale/react";

export function Providers({ children }: { children: React.ReactNode }) {
  return <ClawscaleProvider>{children}</ClawscaleProvider>;
}

The cost: every 16px and 20px path joins your client bundle, about 190 kB gzipped. This site uses sync icons.

Icon components#

Icon components such as DatabaseIcon carry their own paths. They render on the server without sync icons, and tree-shaking bundlers keep only the icons you import. Pass one to any icon prop.

import { Button, ButtonGroup } from "@clawscale/react";
import { DatabaseIcon, PauseIcon, RefreshIcon } from "@clawscale/react/icons";

export default function LoadingIconsComponents() {
  return (
    <ButtonGroup>
      <Button icon={<RefreshIcon />} text="Refresh" />
      <Button icon={<DatabaseIcon />} text="Snapshot" />
      <Button icon={<PauseIcon />} intent="warning" text="Pause ingest" />
    </ButtonGroup>
  );
}

Preloading#

Icons.load loads the paths of the icons you name. Icons that are already loaded render at once, so call it early in client-rendered apps, for example before the first render. It does not fill server-rendered HTML.

Icons.loadAll starts loading every icon in both sizes. Its promise settles before the paths arrive, so awaiting it does not delay rendering.

import { Icons, IconSize } from "@clawscale/react/icons";

// The icons on the first screen, before it renders.
await Icons.load(["database", "refresh", "warning-sign"], IconSize.STANDARD);

// Or every icon, in the background.
Icons.loadAll();

Icons.setLoaderOptions({ loader: "all" }) makes the default loader fetch both sizes at once.

Comparison#

ApproachServer HTMLBundle
Default loadingPlaceholder, SVG after hydrationPaths load on first use, one set per size
Sync iconsSVGEvery path, about 190 kB gzipped
Icon componentsSVGOnly the icons you import
PreloadingPlaceholderThe paths you load, when you load them