Frameworks

Icons in Next.js

glyphfox funktioniert in beiden Routern. Im App Router benötigen die React-Komponenten eine Client-Boundary aus einer einzigen Datei, und Server Components können dieselben Icons als reines SVG ausgeben, das keinerlei JavaScript mitliefert.

Installation

Installieren Sie das Paket. Es wird als kompiliertes ESM und CommonJS mit Typdeklarationen ausgeliefert, daher müssen Sie in next.config.js nichts ergänzen: kein transpilePackages, keinen Loader und kein Stylesheet.

terminal
npm install glyphfox

Die React-Komponenten stammen aus glyphfox/react und werden in der React-Anleitung ausführlich behandelt. Diese Seite zeigt, was sich in Next.js ändert: wo Komponenten laufen dürfen, wie Sie Icons aus dem Client-Bundle heraushalten und was der Server rendert.

Warum der App Router eine Client-Boundary braucht

Im App Router ist jedes Modul eine Server Component, es sei denn, die Datei ist mit "use client" gekennzeichnet. Die glyphfox-Komponenten verwalten ihren Zustand für Hover, Klick und Morphing mit useState, useEffect und useRef, und das Paket enthält keine "use client"-Direktive. Wenn Sie <HomeIcon /> direkt in app/page.tsx rendern, scheitert das deshalb mit einer Fehlermeldung, dass Hooks nur in Client Components funktionieren.

Die Lösung besteht nicht darin, ganze Seiten zu Client Components zu machen. Kennzeichnen Sie eine kleine Datei als Boundary und importieren Sie Ihre Icons über diese Datei. Server Components können Client Components rendern; die Icons werden trotzdem auf dem Server zu HTML gerendert und anschließend im Browser hydriert.

Eine Client-Datei für Ihre Icons

components/icons.ts
'use client';

export { HomeIcon, SearchIcon, BellIcon, MenuIcon, Icon, MorphIcon, Fusion } from 'glyphfox/react';
app/page.tsx
import { HomeIcon, SearchIcon, Fusion } from '@/components/icons';

export default function Page() {
  return (
    <header className="bar">
      <a href="/"><HomeIcon size={20} /> Home</a>
      <SearchIcon title="Search" />
      <Fusion icons={['cloud', 'lock']} layout="overlap" />
    </header>
  );
}

Re-exportieren Sie die Namen einzeln. Next.js unterstützt export * in einer Client-Boundary nicht, und benannte Exporte erhalten das Tree-Shaking: Nur die in der Datei aufgeführten Icons landen in den Client-Chunks, und davon auch nur die, die eine Seite tatsächlich verwendet.

In einer Datei, die bereits mit "use client" beginnt, können Sie direkt aus glyphfox/react importieren. Die Boundary-Datei ist nur für Icons gedacht, die Sie aus Server-Dateien rendern.

Interaktive Icons in Client Components

Event-Handler und Refs lassen sich nicht von einer Server Component an eine Client Component übergeben, weil Props an dieser Grenze serialisierbar sein müssen. Alles, was auf Benutzeraktionen reagiert, etwa ein Umschalter mit Morph, gehört in eine eigene Client Component:

components/theme-toggle.tsx
'use client';

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

export function ThemeToggle() {
  const [dark, setDark] = useState(true);
  return (
    <button
      type="button"
      onClick={() => setDark((d) => !d)}
      aria-label={dark ? 'Switch to the light theme' : 'Switch to the dark theme'}
    >
      <MorphIcon name={dark ? 'moon' : 'sun'} duration={300} />
    </button>
  );
}

Der Server rendert das Ausgangs-Icon, und das Morphing läuft im Browser ab, sobald die Komponente hydriert ist. Halten Sie den ersten Zustand auf beiden Seiten identisch: Wenn Sie beim Rendern localStorage oder die Uhrzeit auslesen, weichen Server und Client voneinander ab, und React meldet einen Hydration-Mismatch.

Icons ohne JavaScript in Server Components

Die meisten Icons ändern sich nach dem Laden der Seite nie. Für diese Icons kann eine Server Component die Icon-Daten mit renderToString aus glyphfox/core in einen SVG-String umwandeln. Der Code läuft nur auf dem Server, daher kommen die Icons als Markup an und vergrößern das Client-Bundle nicht.

components/server-icon.tsx
import { renderToString } from 'glyphfox/core';
import type { IconData, SvgOptions } from 'glyphfox';

type Props = SvgOptions & { icon: IconData };

export function ServerIcon({ icon, ...options }: Props) {
  return <span className="icon" dangerouslySetInnerHTML={{ __html: renderToString(icon, options) }} />;
}
app/docs/page.tsx
import fileText from 'glyphfox/icons/file-text';
import externalLink from 'glyphfox/icons/external-link';
import { ServerIcon } from '@/components/server-icon';

export default function Docs() {
  return (
    <a href="https://example.com/handbook">
      <ServerIcon icon={fileText} size={18} /> Handbook
      <ServerIcon icon={externalLink} size={14} title="opens in a new tab" />
    </a>
  );
}

Jeder Import von glyphfox/icons/NAME ist ein kleines Datenmodul. Wenn Sie mit Namen und der vollständigen Namenssyntax arbeiten möchten (file+check, cloud&lock:side, heart/solid), verwenden Sie stattdessen toSvg aus dem Haupteinstiegspunkt. Es bringt die gesamte Icon-Registry mit, doch in einer Server Component belastet das nur den Server:

app/status/page.tsx
import { toSvg } from 'glyphfox';

export default function Status() {
  return <span className="icon" dangerouslySetInnerHTML={{ __html: toSvg('file+check', { size: 20 }) }} />;
}

Die Optionen tragen die Namen des Cores: class statt className und style als String. Für dangerouslySetInnerHTML ist ein Wrapper-Element nötig; sorgen Sie dafür, dass es sich wie das Icon selbst verhält, damit es keine zusätzliche Zeilenhöhe erzeugt:

app/globals.css
.icon {
  display: inline-flex;
  line-height: 0;
  vertical-align: middle;
}

Lebendige Zustände funktionieren auch ohne JavaScript, weil die Animation ein kleines Stylesheet im SVG ist: renderToString(loader, { animate: 'spin' }) rotiert, und renderToString(menu, { morphTo: x, trigger: 'hover' }) morpht beim Hover per CSS-Transition. Auslöser für Klick und Zustand benötigen eine Client Component.

Icons per Name auf dem Server

Icon, morphTo und Fusion schlagen Namen in einer Registry nach, die jede importierte Icon-Komponente befüllt. Ein Name, den niemand importiert hat, wird nach dem Laden der Seite per dynamischem Import nachgeladen. Der Server sendet dafür also ein leeres SVG, und das Icon taucht erst nach der Hydration auf. Registrieren Sie die Daten aller Namen, die Sie aus Strings rendern, in der Boundary-Datei; sie läuft während des Renderings auf dem Server und erneut im Browser:

components/icons.ts
'use client';

import { registerIcons } from 'glyphfox/react';
import { iconSettings, iconUsers, iconChartBar } from 'glyphfox';

registerIcons([iconSettings, iconUsers, iconChartBar]);

export { Icon, HomeIcon } from 'glyphfox/react';

Die Daten-Exporte unterliegen wie die Komponenten dem Tree-Shaking. Achten Sie auf einen Unterschied zwischen den Umgebungen: Entwicklungs-Builds werten unter Umständen jedes Icon-Modul aus. Eine fehlende Registrierung kann daher mit next dev unauffällig bleiben und erst nach next build leer gerendert werden, weil dort ungenutzte Module entfallen.

Pages Router

Der Pages Router kennt keine Server Components, daher funktionieren die Komponenten unverändert in Seiten, Layouts und _app. Jede Seite wird auf dem Server gerendert und hydriert, deshalb ist die obige Registrierung hier genauso nützlich: Rufen Sie registerIcons in pages/_app.tsx auf, wenn Sie Icons per Name rendern.

pages/_app.tsx
import type { AppProps } from 'next/app';
import { registerIcons } from 'glyphfox/react';
import { iconSettings, iconUsers } from 'glyphfox';

registerIcons([iconSettings, iconUsers]);

export default function App({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}

Ein Icon als Favicon

Der App Router erkennt app/icon.svg als Website-Icon. Die CLI schreibt jedes beliebige Icon mit einer expliziten Farbe dorthin, denn ein Favicon hat keine umgebende Textfarbe, an der sich currentColor orientieren könnte:

terminal
npx glyphfox copy fox --color "#ff7a1a" --weight 2.5 --out app/icon.svg

Für PNG-App-Icons in mehreren Größen exportiert das Brand-Kit im Studio ein Favicon-Set, abgestimmt auf Ihre Farbe, Strichstärke und Ihren Stil.

Was die Build-Ausgabe enthält

Mit webpack erzeugt next build viele winzige Client-Chunks, ungefähr einen pro Icon im Set. Sie stammen vom On-Demand-Loader hinter Icon und morphTo: Sein dynamischer Import zeigt auf den Icon-Ordner, daher legt der Bundler für jedes Icon, das per Name angefordert werden könnte, einen Chunk an. Jeder ist nur einige hundert Byte groß und wird nur heruntergeladen, wenn eine Seite einen Namen rendert, der nicht importiert wurde. Unabhängig vom Bundler gilt: Wenn Sie die Namen registrieren, die Sie aus Strings rendern (siehe oben), werden diese Chunks nie angefordert. Die Icons, die Sie direkt importieren, liegen in Ihren regulären Chunks. Messwerte finden Sie in der Anleitung zum Tree-Shaking.

Props

Die Komponenten akzeptieren dieselben Props wie in jeder React-App. Wenn eine Server Component sie über die Boundary rendert, sind alle unten aufgeführten Props einfache Werte und können die Boundary passieren; nur Callbacks wie onClick sowie ref müssen innerhalb einer Client Component gesetzt werden. Die Core-Optionen von renderToString und toSvg tragen dieselben Namen.

PropTypStandardBeschreibung
sizenumber, string24Breite und Höhe; Zahlen sind Pixel.
colorstringcurrentColorHauptfarbe.
secondaryColorstringcolorFarbe der Duotone-Ebene und der Solid-Füllung.
secondaryOpacitynumber0.3 / 1Deckkraft der Duotone-Ebene oder der Solid-Füllung.
weightnumber2Strichstärke von 1 bis 3 im 24er-Raster, unter 24 px optisch ausgeglichen.
absoluteWeightbooleanfalseHält den Strich bei jeder Größe auf weight Pixeln.
cornersround, sharproundAbgerundete oder spitze Verbindungen und Enden.
variantoutline, duotone, solidoutlineStilachse; solid entsteht aus den geschlossenen Formen.
rotatenumber0Drehung in Grad.
flipXbooleanfalseHorizontal spiegeln.
flipYbooleanfalseVertikal spiegeln.
mirrorRtlbooleanfalseIn Layouts von rechts nach links spiegeln.
animatedraw, pop, wiggle, spin, pulse, bounce, shakekeinerVoreinstellung für den lebendigen Zustand.
triggermount, hover, click, statemountWann die Animation oder der Morph abläuft.
activebooleanfalseDer Zustand für den Auslöser state.
durationnumber (ms)je nach VoreinstellungDauer der Animation oder des Morphs.
delaynumber (ms)0Verzögerung der Animation oder des Morphs.
loopboolean, numberfalseEndlos oder eine bestimmte Anzahl von Malen wiederholen (spin und pulse laufen standardmäßig in Schleife).
modifierplus, minus, check, x, alert, lock, clock, sync, arrow-up, arrow-down, dotkeinerBadge mit automatischer Aussparung.
modifierCornertop-left, top-right, bottom-left, bottom-rightbottom-rightEcke des Badges.
morphToname, datakeinerZiel des Morphs; der Auslöser bestimmt, wann es erscheint.
titlestringkeinerZugänglicher Name: title, role img und aria-label.
decorativebooleantrue ohne titleVerbirgt das Icon vor assistiven Technologien.

Stolperfallen

Hook-Fehler in Server Components
Ein Import aus glyphfox/react in einer Server-Datei schlägt fehl, weil die Komponenten Hooks verwenden. Importieren Sie über eine Datei mit "use client", oder rendern Sie statisches SVG mit renderToString.
export * in der Boundary
Next.js lehnt export * in einer Client-Boundary ab. Führen Sie die Icons als benannte Exporte auf.
Funktionen als Props vom Server
onClick, onMouseEnter und Refs sind nicht serialisierbar. Verlagern Sie interaktive Icons in eine Client Component und reichen Sie nur Daten nach unten weiter.
Leere Icons nach next build
Ein Icon-Name, der nie importiert wurde, wird auf dem Server leer gerendert. Registrieren Sie seine Daten mit registerIcons in der Boundary-Datei oder in _app.
Unbekannte Namen werfen Fehler
toSvg wirft bei einem nicht existierenden Namen einen Fehler, der zu einem Serverfehler wird. Gleichen Sie von Nutzern übergebene Namen vor dem Rendern mit iconNames aus glyphfox ab.
Zusätzlicher Abstand um Server-Icons
Der Wrapper für dangerouslySetInnerHTML ist ein Inline-Element mit eigener Zeilenbox. Geben Sie ihm display: inline-flex und line-height: 0.

Häufige Fragen

Brauche ich transpilePackages für glyphfox?

Nein. Das Paket liefert reines JavaScript als ESM und CommonJS mit Typdeklarationen, daher löst Next.js es wie jede andere kompilierte Abhängigkeit auf und bündelt es.

Kann ich die Web Component glyph-fox in Next.js verwenden?

Ja, importieren Sie glyphfox/web in einer Client Component. Das Modul lässt sich beim serverseitigen Rendering gefahrlos auswerten, aber ein Custom Element zeichnet erst, nachdem der Browser sein Upgrade durchgeführt hat. Das Server-HTML enthält daher an der Stelle des Icons ein leeres Element. In einer React-App sind die Komponenten die bessere Wahl, weil ihr SVG bereits im Server-HTML steht; die Anleitung zu Web Components beschreibt das Element.

Icons in diesem Leitfaden