Recipe

Next.js App Router with Server Components

Render icons in Server Components with no JavaScript at all, and reach for client components only where an icon reacts to clicks or morphs between names.

Server Components

app/page.tsx
import { Icon, Fusion } from 'glyphfox/react/server';

export default function Page() {
  return (
    <main>
      <h1>
        <Icon name="home" size={28} className="title-icon" /> Dashboard
      </h1>
      <Icon name="file+check@tr" variant="duotone" title="Saved" />
      <Icon name="bell" animate="shake" trigger="hover" />
      <Icon name="menu" morphTo="x" trigger="hover" />
      <Fusion icons={['cloud', 'lock']} layout="overlap" />
    </main>
  );
}

glyphfox/react/server exports hook free Icon and Fusion components. They render static SVG during the server render, so nothing is sent to the browser for them, and every icon of the set is available by name. Living states, hover and state morphs travel inside the SVG as CSS.

Client components

app/like-button.tsx
'use client';

import { useState } from 'react';
import { HeartIcon, MorphIcon } from 'glyphfox/react';

export function LikeButton() {
  const [liked, setLiked] = useState(false);
  return (
    <button onClick={() => setLiked(!liked)} aria-pressed={liked}>
      <HeartIcon animate="pop" trigger="state" active={liked} variant={liked ? 'solid' : 'outline'} />
      <MorphIcon name={liked ? 'check' : 'heart'} />
    </button>
  );
}

The modules of glyphfox/react are marked 'use client', so the icon components also work when a Server Component renders them directly: they are server rendered and hydrated. Use them where you need the click trigger, morph tweens or useMorph.

Bundle size

next.config.mjs
/** @type {import('next').NextConfig} */
export default {
  experimental: {
    optimizePackageImports: ['glyphfox'],
  },
};

Each icon is its own module. With optimizePackageImports Next.js rewrites barrel imports to those modules; deep imports such as import { FileIcon } from 'glyphfox/react/icons/file' work everywhere without configuration.

Sprite

terminal
npx glyphfox build home search file+check --type sprite --out public/icons
app/nav.tsx
export function SpriteGlyph({ name }: { name: string }) {
  return (
    <svg width="20" height="20" aria-hidden="true">
      <use href={`/icons/sprite.svg#gf-${name}`} />
    </svg>
  );
}

For long lists of static icons, a sprite of just the icons you use keeps the HTML small and caches across pages.