Frameworks

Icons in React Native

React Native hat kein eigenes SVG-Element, daher werden glyphfox-Icons über react-native-svg gerendert. Die glyphfox-Engine wandelt Icon-Daten in einen SVG-String um, und SvgXml zeichnet ihn nativ auf iOS und Android.

Installation

Installieren Sie glyphfox und react-native-svg. In einem Expo-Projekt überlassen Sie Expo die Wahl der react-native-svg-Version, die zu Ihrem SDK passt; in einem reinen React-Native-Projekt installieren Sie anschließend die iOS-Pods.

terminal
npm install glyphfox react-native-svg
# Expo
npx expo install react-native-svg
# bare React Native, iOS
npx pod-install

Die React-Komponenten aus glyphfox/react sind nicht für React Native gedacht: Sie rendern DOM-Elemente vom Typ <svg>, und React Native hat keine Host-Komponente mit diesem Namen. Verwenden Sie die unten beschriebene Core-Engine.

So funktioniert es

renderToString(icon, options) aus glyphfox/core nimmt Icon-Daten und die gemeinsamen Props entgegen und gibt einen vollständigen SVG-String zurück. Alles, was die Geometrie verändert, geschieht in JavaScript, bevor der String entsteht: die Strichstärkenachse mit ihrer optischen Kompensation, abgerundete oder spitze Ecken, die Stile duotone und solid, Drehung und Spiegelung, Modifier-Badges mit ihrer Aussparung sowie Fusionen. react-native-svg parst den String anschließend einmal und zeichnet native Formen.

Was nicht erhalten bleibt, ist alles, was auf CSS im Browser angewiesen ist: die Animationen der lebendigen Zustände, der reine CSS-Morph und die Klasse für die Spiegelung bei Schreibrichtung von rechts nach links. Der Abschnitt zu den Einschränkungen weiter unten nennt für jeden dieser Punkte den nativen Ersatz.

Eine kleine Glyph-Komponente

Kapseln Sie SvgXml einmal in einer eigenen Komponente und verwenden Sie diese überall. Der String wird memoisiert, damit ein erneutes Rendern der Elternkomponente das SVG nicht noch einmal parst. Die Farbe geht als Prop an SvgXml, sodass ein Theme-Wechsel den String nicht neu erzeugt.

Glyph.tsx
import { useMemo } from 'react';
import { SvgXml } from 'react-native-svg';
import { renderToString } from 'glyphfox/core';
import type { IconData, SvgOptions } from 'glyphfox';

type Props = Omit<SvgOptions, 'size' | 'color'> & { icon: IconData; size?: number; color?: string };

export function Glyph({ icon, size = 24, color = '#121014', ...options }: Props) {
  const key = JSON.stringify(options);
  const xml = useMemo(() => renderToString(icon, { ...options, size }), [icon, size, key]);
  return <SvgXml xml={xml} width={size} height={size} color={color} />;
}
Toolbar.tsx
import { View } from 'react-native';
import home from 'glyphfox/icons/home';
import bell from 'glyphfox/icons/bell';
import folder from 'glyphfox/icons/folder';
import { Glyph } from './Glyph';

export function Toolbar() {
  return (
    <View style={{ flexDirection: 'row', gap: 16 }}>
      <Glyph icon={home} size={22} color="#ff7a1a" />
      <Glyph icon={bell} modifier="dot" weight={1.5} />
      <Glyph icon={folder} variant="duotone" />
    </View>
  );
}

Die Striche verwenden currentColor, und react-native-svg löst den Wert über die Prop color auf. Folgen Sie dem System-Theme mit useColorScheme() und übergeben Sie eine andere Farbe; der zwischengespeicherte SVG-String bleibt derselbe.

Das Bundle klein halten

Metro beherrscht kein Tree-Shaking. Alles, was ein Modul importiert, landet im App-Bundle, daher entscheidet die Art, wie Sie Icons importieren, darüber, wie viel ausgeliefert wird:

ImportWas ausgeliefert wirdGeeignet für
glyphfox/core plus glyphfox/icons/NAMEDie Engine und die importierten Icons, einige hundert Byte pro IconDie meisten Apps
toSvg aus glyphfoxDie Engine und alle Icons des SetsPrototypen oder Icons, die zur Laufzeit per Name gewählt werden
Ein mit der CLI gebautes Subset-PaketNur fertige SVG-Strings, überhaupt keine EngineEine feste Auswahl an Icons mit festen Props

Die CLI macht aus einer Liste von Icons ein kleines Paket, dessen Exporte einfache SVG-Strings sind, mit bereits zur Build-Zeit eingebrannten Props. Binden Sie den Ordner als Workspace- oder file:-Abhängigkeit in Ihre App ein und übergeben Sie die Strings an SvgXml. Bindestriche in Namen werden zu Unterstrichen, arrow-right wird also als arrow_right exportiert:

terminal
npx glyphfox build home search bell arrow-right --type package --name @acme/icons --weight 1.75 --out packages/icons
Header.tsx
import { SvgXml } from 'react-native-svg';
import { home, arrow_right } from '@acme/icons';

export function Header() {
  return (
    <>
      <SvgXml xml={home} width={24} height={24} color="#121014" />
      <SvgXml xml={arrow_right} width={20} height={20} color="#121014" />
    </>
  );
}

Metro und Paket-Exports

glyphfox/core und glyphfox/icons/NAME sind Subpath-Exports, die im Feld exports des Pakets deklariert sind. Metro liest dieses Feld ab React Native 0.79 standardmäßig. Aktivieren Sie die Unterstützung bei älteren Versionen in der Metro-Konfiguration, sonst schlagen diese Importe mit dem Fehler „Unable to resolve module“ fehl:

metro.config.js
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');

module.exports = mergeConfig(getDefaultConfig(__dirname), {
  resolver: { unstable_enablePackageExports: true },
});

Übergeben Sie der Engine immer Icon-Daten, keine Namen. Die Web-Pakete laden unbekannte Namen über einen dynamischen Import des Icon-Ordners; das funktioniert in Web-Bundlern, Metro folgt solchen Importen aber nicht. Liegen die Daten bereits vor, muss nie etwas nachgeladen werden.

Stile, Modifier und Fusionen

Auch die Kompositionswerkzeuge des Cores arbeiten mit Daten. fuse verschmilzt 2 bis 10 Icons zu einem neuen Icon, das weiterhin den Icon-Regeln folgt, und alle folgenden Optionen werden beim Erzeugen des Strings angewendet:

Sync.tsx
import { fuse } from 'glyphfox/core';
import cloud from 'glyphfox/icons/cloud';
import lock from 'glyphfox/icons/lock';
import heart from 'glyphfox/icons/heart';
import { Glyph } from './Glyph';

const cloudLock = fuse([cloud, lock], { layout: 'overlap' });

export function Sync({ synced }: { synced: boolean }) {
  return (
    <>
      <Glyph icon={cloudLock} size={28} />
      <Glyph icon={cloud} modifier={synced ? 'check' : 'sync'} modifierCorner="top-right" />
      <Glyph icon={heart} variant="solid" corners="sharp" />
    </>
  );
}

Erzeugen Sie Fusionen auf Modulebene oder memoisieren Sie sie: fuse berechnet die Geometrie der Aussparungen, was einmalig günstig, bei jedem Rendern aber verschwenderisch ist.

Einen Morph animieren

Es gibt kein CSS, das einen Übergang ausführen könnte, aber die Morph-Engine selbst ist reines JavaScript. createMorph(from, to) bereitet einen Morph einmalig vor und nutzt dabei automatisch die von Hand abgestimmten Paare wie menu zu x und play zu pause. frame(t) liefert Icon-Daten für jeden Fortschritt von 0 bis 1, und tween aus dem Core treibt den Fortschritt mit requestAnimationFrame an:

MenuButtonIcon.tsx
import { useEffect, useRef, useState } from 'react';
import { AccessibilityInfo } from 'react-native';
import { SvgXml } from 'react-native-svg';
import { createMorph, renderToString, tween } from 'glyphfox/core';
import menu from 'glyphfox/icons/menu';
import x from 'glyphfox/icons/x';

const morph = createMorph(menu, x);

export function MenuButtonIcon({ open, color = '#121014' }: { open: boolean; color?: string }) {
  const [t, setT] = useState(open ? 1 : 0);
  const current = useRef(t);
  current.current = t;

  useEffect(() => {
    let alive = true;
    let cancel = () => {};
    AccessibilityInfo.isReduceMotionEnabled().then((reduce) => {
      if (alive) cancel = tween(current.current, open ? 1 : 0, { duration: reduce ? 0 : 280 }, setT);
    });
    return () => {
      alive = false;
      cancel();
    };
  }, [open]);

  return <SvgXml xml={renderToString(morph.frame(t), { size: 24 })} width={24} height={24} color={color} />;
}

Jeder Frame erzeugt und parst einen neuen String, was für ein einzelnes Icon über einige hundert Millisekunden unproblematisch ist. Vermeiden Sie viele gleichzeitige Morphs, und setzen Sie lang laufende Bewegungen wie einen Spinner stattdessen über eine native Transformation einer umgebenden View um.

Barrierefreiheit

Die Attribute title, role und aria im SVG-String erreichen den nativen Accessibility-Tree nicht. Setzen Sie die Accessibility-Props daher an der Komponente, die das Icon umgibt. Ein Icon in einem beschrifteten Button braucht nichts weiter; ein eigenständiges Icon, das Bedeutung trägt, wird zu einem Bild mit Label:

Status.tsx
import { Pressable, View } from 'react-native';
import search from 'glyphfox/icons/search';
import cloud from 'glyphfox/icons/cloud';
import { Glyph } from './Glyph';

export function Status({ onSearch }: { onSearch: () => void }) {
  return (
    <>
      <Pressable accessibilityRole="button" accessibilityLabel="Search" onPress={onSearch}>
        <Glyph icon={search} />
      </Pressable>
      <View accessible accessibilityRole="image" accessibilityLabel="All changes synced">
        <Glyph icon={cloud} modifier="check" />
      </View>
    </>
  );
}

Die allgemeinen Regeln dazu, wann ein Icon ein Label braucht, finden Sie im Leitfaden zur Barrierefreiheit.

Was sich nicht übertragen lässt

FunktionIm WebUnter React Native
Lebendige Zustände (animate)Keyframes in einem <style>-Element im SVGSvgXml ignoriert das style-Element. Animieren Sie eine umgebende View mit Animated oder Reanimated, zum Beispiel mit einer Rotation für einen Spinner.
Reiner CSS-Morph (morphTo mit Auslöser)Eine CSS-Transition der PfaddatenRendern Sie Frames mit createMorph, wie oben gezeigt.
mirrorRtlEine Klasse, die das Icon innerhalb von dir="rtl" spiegeltÜbergeben Sie für richtungsabhängige Icons flipX={I18nManager.isRTL}.
title und decorativeEin title-Element, role="img" und aria-hiddenAccessibility-Props am Wrapper.
Relative Größen"1em", "2rem"Nur Zahlen.

Props

Diese Optionen von renderToString und toSvg verändern die Zeichnung selbst und funktionieren in React Native. Sie haben dieselben Namen und dieselbe Bedeutung wie die Props aller anderen glyphfox-Pakete.

OptionTypStandardBeschreibung
sizenumber, string24Breite und Höhe; Zahlen sind Pixel.
colorstringcurrentColorHauptfarbe.
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.
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.

Stolperfallen

glyphfox/react verwenden
Die Komponenten für das Web rendern DOM-Elemente, daher bricht React Native mit einem Fehler über eine fehlende View-Config für svg ab. Rendern Sie stattdessen über SvgXml.
Das komplette Set im Bundle
import { toSvg } from 'glyphfox' zieht alle Icons in die App, weil Metro alles behält. Importieren Sie Icon-Daten einzeln, oder bauen Sie ein Subset-Paket.
glyphfox/core lässt sich nicht auflösen
Vor React Native 0.79 sind Paket-Exports in Metro deaktiviert. Aktivieren Sie unstable_enablePackageExports.
Parsen bei jedem Rendern
Ein Inline-Aufruf von renderToString in einer langen Liste erzeugt und parst pro Zeile und pro Rendern einen String. Memoisieren Sie den String wie die Glyph-Komponente, oder verwenden Sie vorgefertigte Strings.
Unbekannte Namen
toSvg wirft bei einem Namen, der nicht existiert, einen Fehler. Prüfen Sie Namen, die von einem Server kommen, gegen iconNames, oder ordnen Sie sie selbst importierten Daten zu.

Häufige Fragen

Funktioniert das mit Expo?

Ja. Installieren Sie react-native-svg mit npx expo install react-native-svg und verwenden Sie dieselbe Glyph-Komponente. Expo nutzt Metro, daher gilt der Hinweis zu den Paket-Exports: SDKs auf Basis von React Native 0.79 oder neuer brauchen keine Änderung.

Wie erstelle ich einen Lade-Spinner?

Zeichnen Sie loader oder loader-circle mit der Glyph-Komponente und drehen Sie eine umgebende Animated.View mit einer endlos wiederholten Timing-Animation. Der Native Driver sorgt für eine flüssige Bewegung, und der SVG-String wird nur einmal erzeugt.

Icons in diesem Leitfaden