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.
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.
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} />; }
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ón | Qué se incluye | Úsalo para |
|---|---|---|
glyphfox/core más glyphfox/icons/NAME | El motor y los iconos que importes, unos cientos de bytes por icono | La mayoría de las aplicaciones |
toSvg de glyphfox | El motor y todos los iconos del conjunto | Prototipos, o iconos elegidos por nombre en tiempo de ejecución |
| Un paquete de subconjunto generado con la CLI | Solo cadenas SVG ya generadas, sin ningún motor | Un 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:
npx glyphfox build home search bell arrow-right --type package --name @acme/icons --weight 1.75 --out packages/icons
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:
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:
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:
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:
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ística | En la web | En React Native |
|---|---|---|
Estados vivos (animate) | Keyframes en un elemento <style> dentro del SVG | SvgXml 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 path | Renderiza fotogramas con createMorph, como se muestra arriba. |
mirrorRtl | Una clase que voltea el icono dentro de dir="rtl" | Pasa flipX={I18nManager.isRTL} en los iconos direccionales. |
title y decorative | Un elemento title, role="img" y aria-hidden | Props 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ón | Tipo | Por defecto | Descripción |
|---|---|---|---|
size | number, string | 24 | Ancho y alto; los números son píxeles. |
color | string | currentColor | Color principal. |
secondaryOpacity | number | 0.3 / 1 | Opacidad de la capa duotone o del relleno sólido. |
weight | number | 2 | Trazo de 1 a 3 en la cuadrícula de 24, con compensación óptica por debajo de 24 px. |
absoluteWeight | boolean | false | Mantiene el trazo en weight píxeles a cualquier tamaño. |
corners | round, sharp | round | Uniones y extremos redondeados o rectos. |
variant | outline, duotone, solid | outline | Eje de estilo; solid se obtiene de las formas cerradas. |
rotate | number | 0 | Rotación en grados. |
flipX | boolean | false | Refleja en horizontal. |
flipY | boolean | false | Refleja en vertical. |
modifier | plus, minus, check, x, alert, lock, clock, sync, arrow-up, arrow-down, dot | ninguno | Insignia con hueco de recorte automático. |
modifierCorner | top-left, top-right, bottom-left, bottom-right | bottom-right | Esquina 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 deSvgXmlen 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
renderToStringen 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
toSvglanza un error si el nombre no existe. Valida contraiconNameslos 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.