Frameworks

Iconos en React Native

React Native no tiene un elemento SVG propio, así que los iconos de glyphfox se renderizan a través de react-native-svg. El motor de glyphfox convierte los datos del icono en una cadena SVG y SvgXml la dibuja de forma nativa en iOS y Android.

Instalación

Instala glyphfox y react-native-svg. En un proyecto de Expo, deja que Expo elija la versión de react-native-svg que corresponde a tu SDK; en un proyecto bare, instala después los pods de iOS.

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

Los componentes de React que incluye glyphfox/react no sirven para React Native: renderizan elementos <svg> del DOM, y React Native no tiene ningún componente host con ese nombre. Usa el motor del núcleo, que se describe a continuación.

Cómo funciona

renderToString(icon, options) de glyphfox/core recibe los datos del icono y las props compartidas y devuelve una cadena SVG completa. Todo lo que modifica la geometría ocurre en JavaScript antes de que exista la cadena: el eje de peso con su compensación óptica, las esquinas redondeadas o rectas, los estilos duotone y solid, la rotación y los volteos, las insignias de modificador con su hueco de recorte, y las fusiones. Después, react-native-svg analiza la cadena una sola vez y dibuja formas nativas.

Lo que no sobrevive es todo aquello que depende del CSS de un navegador: las animaciones de los estados vivos, el morph solo con CSS y la clase de reflejo para interfaces de derecha a izquierda. La sección sobre limitaciones, más abajo, enumera cada caso con su alternativa nativa.

Un pequeño componente Glyph

Envuelve SvgXml una vez y úsalo en todas partes. La cadena se memoiza, así que cuando el padre se vuelve a renderizar el SVG no se analiza de nuevo, y el color se pasa a SvgXml como prop para que un cambio de tema no reconstruya la cadena.

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>
  );
}

Los trazos usan currentColor, y react-native-svg lo resuelve a partir de la prop color. Sigue el tema del sistema con useColorScheme() y pasa otro color; la cadena SVG en caché sigue siendo la misma.

Mantén el bundle pequeño

Metro no aplica tree shaking. Todo lo que importa un módulo acaba en el bundle de la aplicación, así que la forma de importar los iconos determina cuánto se incluye:

ImportaciónQué se incluyeÚsalo para
glyphfox/core más glyphfox/icons/NAMEEl motor y los iconos que importes, unos cientos de bytes por iconoLa mayoría de las aplicaciones
toSvg de glyphfoxEl motor y todos los iconos del conjuntoPrototipos, o iconos elegidos por nombre en tiempo de ejecución
Un paquete de subconjunto generado con la CLISolo cadenas SVG ya generadas, sin ningún motorUn conjunto fijo de iconos con props fijas

La CLI convierte una lista de iconos en un paquete pequeño cuyas exportaciones son cadenas SVG simples, con las props incorporadas en tiempo de compilación. Añade la carpeta a tu aplicación como dependencia de workspace o de tipo file: y pasa las cadenas a SvgXml. Los guiones de los nombres se convierten en guiones bajos, así que arrow-right se exporta como arrow_right:

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 y las exportaciones del paquete

glyphfox/core y glyphfox/icons/NAME son exportaciones de subruta declaradas en el campo exports del paquete. Metro lee ese campo por defecto a partir de React Native 0.79. En versiones anteriores, actívalo en la configuración de Metro; de lo contrario, estas importaciones fallan con el error unable to resolve module:

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

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

Pasa siempre al motor los datos de los iconos, no sus nombres. Los paquetes web cargan los nombres desconocidos con una importación dinámica de la carpeta de iconos, algo que funciona en los bundlers web pero que Metro no sigue; si ya tienes los datos, nunca hace falta cargar nada.

Estilos, modificadores y fusiones

Las herramientas de composición del núcleo también trabajan con datos. fuse combina de 2 a 10 iconos en un icono nuevo que sigue cumpliendo las reglas de los iconos, y todas las opciones siguientes se aplican al generar la cadena:

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" />
    </>
  );
}

Crea las fusiones a nivel de módulo o memoízalas: fuse calcula la geometría del recorte, algo barato una vez pero un desperdicio en cada renderizado.

Animar un morph

No hay CSS al que aplicar una transición, pero el propio motor de morph es JavaScript puro. createMorph(from, to) prepara un morph una sola vez, usando automáticamente los pares ajustados a mano, como menu a x y play a pause, y frame(t) devuelve los datos del icono para cualquier progreso entre 0 y 1. tween, del núcleo, controla el progreso con requestAnimationFrame:

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} />;
}

Cada fotograma genera y analiza una cadena nueva, lo cual está bien para un solo icono durante unos cientos de milisegundos. Evita ejecutar muchos morphs a la vez y, para movimientos prolongados como un spinner, usa en su lugar una transformación nativa de una vista contenedora.

Accesibilidad

Los atributos title, role y aria de la cadena SVG no llegan al árbol de accesibilidad nativo, así que define las props de accesibilidad en el componente que rodea al icono. Un icono dentro de un botón con etiqueta no necesita nada más; un icono independiente que transmite significado se convierte en una imagen con etiqueta:

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>
    </>
  );
}

Las reglas generales sobre cuándo necesita etiqueta un icono están en la guía de accesibilidad.

Lo que no se traslada

CaracterísticaEn la webEn React Native
Estados vivos (animate)Keyframes en un elemento <style> dentro del SVGSvgXml ignora el elemento style. Anima una vista contenedora con Animated o Reanimated, por ejemplo con una rotación para un spinner.
Morph solo con CSS (morphTo con un disparador)Una transición CSS de los datos del pathRenderiza fotogramas con createMorph, como se muestra arriba.
mirrorRtlUna clase que voltea el icono dentro de dir="rtl"Pasa flipX={I18nManager.isRTL} en los iconos direccionales.
title y decorativeUn elemento title, role="img" y aria-hiddenProps de accesibilidad en el contenedor.
Tamaños relativos"1em", "2rem"Solo números.

Props

Estas opciones de renderToString y toSvg modifican el propio dibujo y funcionan en React Native. Tienen los mismos nombres y el mismo significado que las props de todos los demás paquetes de glyphfox.

OpciónTipoPor defectoDescripción
sizenumber, string24Ancho y alto; los números son píxeles.
colorstringcurrentColorColor principal.
secondaryOpacitynumber0.3 / 1Opacidad de la capa duotone o del relleno sólido.
weightnumber2Trazo de 1 a 3 en la cuadrícula de 24, con compensación óptica por debajo de 24 px.
absoluteWeightbooleanfalseMantiene el trazo en weight píxeles a cualquier tamaño.
cornersround, sharproundUniones y extremos redondeados o rectos.
variantoutline, duotone, solidoutlineEje de estilo; solid se obtiene de las formas cerradas.
rotatenumber0Rotación en grados.
flipXbooleanfalseRefleja en horizontal.
flipYbooleanfalseRefleja en vertical.
modifierplus, minus, check, x, alert, lock, clock, sync, arrow-up, arrow-down, dotningunoInsignia con hueco de recorte automático.
modifierCornertop-left, top-right, bottom-left, bottom-rightbottom-rightEsquina de la insignia.

Errores comunes

Usar glyphfox/react
Los componentes para la web renderizan elementos del DOM, así que React Native se detiene con un error que indica que falta la view config de svg. Renderiza a través de SvgXml en su lugar.
El conjunto completo en el bundle
import { toSvg } from 'glyphfox' incluye todos los iconos en la aplicación, porque Metro lo conserva todo. Importa los datos de los iconos uno a uno, o genera un paquete de subconjunto.
No se puede resolver glyphfox/core
Las exportaciones de paquetes están desactivadas en Metro antes de React Native 0.79. Activa unstable_enablePackageExports.
Análisis en cada renderizado
Una llamada en línea a renderToString en una lista larga genera y analiza una cadena por fila y por renderizado. Memoiza la cadena, como hace el componente Glyph, o usa cadenas generadas previamente.
Nombres desconocidos
toSvg lanza un error si el nombre no existe. Valida contra iconNames los nombres que llegan de un servidor, o asígnalos tú mismo a datos importados.

Preguntas frecuentes

¿Funciona con Expo?

Sí. Instala react-native-svg con npx expo install react-native-svg y usa el mismo componente Glyph. Expo usa Metro, así que se aplica la nota sobre las exportaciones de paquetes: los SDK basados en React Native 0.79 o posterior no necesitan ningún cambio.

¿Cómo creo un spinner de carga?

Dibuja loader o loader-circle con el componente Glyph y gira un Animated.View contenedor con una animación de tipo timing en bucle. El driver nativo mantiene la fluidez, y la cadena SVG se genera una sola vez.

Iconos de esta guía