Frameworks

Ícones em React Native

O React Native não tem um elemento SVG próprio, então os ícones do glyphfox são renderizados pelo react-native-svg. O motor do glyphfox transforma os dados do ícone em uma string SVG, e o SvgXml a desenha de forma nativa no iOS e no Android.

Instalação

Instale o glyphfox e o react-native-svg. Em um projeto Expo, deixe o Expo escolher a versão do react-native-svg compatível com o seu SDK; em um projeto bare, instale os pods do iOS em seguida.

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

Os componentes React de glyphfox/react não servem para React Native: eles renderizam elementos <svg> do DOM, e o React Native não tem nenhum host component com esse nome. Use o motor do core, descrito abaixo.

Como funciona

renderToString(icon, options) de glyphfox/core recebe os dados do ícone e as props compartilhadas e retorna uma string SVG completa. Tudo o que muda a geometria acontece em JavaScript antes de a string existir: o eixo de peso com a compensação óptica, cantos redondos ou retos, os estilos duotone e sólido, rotação e espelhamento, selos modificadores com o espaço de recorte, e fusões. Depois, o react-native-svg faz o parse da string uma única vez e desenha formas nativas.

O que não sobrevive é tudo o que depende de CSS em um navegador: as animações de estados vivos, o morph só com CSS e a classe de espelhamento da direita para a esquerda. A seção de limitações, mais abaixo, lista cada um deles com o seu substituto nativo.

Um pequeno componente Glyph

Envolva o SvgXml uma vez e use em todo lugar. A string é memoizada, então renderizar o pai de novo não faz o parse do SVG outra vez, e a cor é passada ao SvgXml como prop, para que uma troca de tema não reconstrua a string.

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

Os traços usam currentColor, e o react-native-svg resolve esse valor a partir da prop color. Acompanhe o tema do sistema com useColorScheme() e passe uma cor diferente; a string SVG em cache continua a mesma.

Mantenha o bundle pequeno

O Metro não faz tree shaking. Tudo o que um módulo importa vai parar no bundle do app, então a forma como você importa os ícones define quanto código é entregue:

ImportaçãoO que é entregueIndicado para
glyphfox/core mais glyphfox/icons/NAMEO motor e os ícones que você importa, algumas centenas de bytes por íconeA maioria dos apps
toSvg de glyphfoxO motor e todos os ícones do conjuntoProtótipos, ou ícones escolhidos pelo nome em tempo de execução
Um pacote de subconjunto gerado com a CLISó strings SVG prontas, sem motor nenhumUm conjunto fixo de ícones com props fixas

A CLI transforma uma lista de ícones em um pacote pequeno cujos exports são strings SVG simples, com as props já aplicadas no build. Adicione a pasta ao seu app como workspace ou como dependência file: e passe as strings para o SvgXml. Hifens nos nomes viram underscores, então arrow-right é exportado 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 e package exports

glyphfox/core e glyphfox/icons/NAME são subpath exports declarados no campo exports do pacote. O Metro lê esse campo por padrão a partir do React Native 0.79. Em versões anteriores, ative essa leitura na configuração do Metro; caso contrário, esses imports falham com um erro de "unable to resolve module":

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

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

Sempre passe dados de ícone, e não nomes, para o motor. Os pacotes web carregam nomes desconhecidos com um import dinâmico da pasta de ícones, o que funciona em bundlers web, mas não é algo que o Metro acompanhe; com os dados em mãos, nada precisa ser carregado.

Estilos, modificadores e fusões

As ferramentas de composição do core também trabalham com dados. fuse combina de 2 a 10 ícones em um ícone novo que continua seguindo as regras dos ícones, e todas as opções abaixo são aplicadas quando a string é gerada:

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

Crie as fusões no nível do módulo ou memoize: fuse calcula a geometria do recorte, o que é barato uma vez, mas um desperdício a cada renderização.

Animando um morph

Não há CSS para fazer a transição, mas o próprio motor de morph é JavaScript puro. createMorph(from, to) prepara um morph uma única vez, usando automaticamente os pares ajustados à mão, como menu para x e play para pause, e frame(t) retorna os dados do ícone para qualquer progresso de 0 a 1. tween, do core, conduz o progresso com 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 frame gera e faz o parse de uma string nova, o que não é problema para um único ícone durante algumas centenas de milissegundos. Evite rodar muitos morphs ao mesmo tempo e, para movimentos longos como um spinner, use uma transformação nativa em uma view que envolve o ícone.

Acessibilidade

Os atributos title, role e aria dentro da string SVG não chegam à árvore de acessibilidade nativa, então defina as props de acessibilidade no componente em volta do ícone. Um ícone dentro de um botão com rótulo não precisa de mais nada; um ícone isolado que carrega significado vira uma imagem com rótulo:

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

As regras gerais sobre quando um ícone precisa de rótulo estão no guia de acessibilidade.

O que fica de fora

RecursoNa webNo React Native
Estados vivos (animate)Keyframes em um elemento <style> dentro do SVGO SvgXml ignora o elemento style. Anime uma view em volta do ícone com Animated ou Reanimated, por exemplo uma rotação para um spinner.
Morph só com CSS (morphTo com um gatilho)Uma transição CSS dos dados do pathRenderize frames com createMorph, como mostrado acima.
mirrorRtlUma classe que espelha o ícone dentro de dir="rtl"Passe flipX={I18nManager.isRTL} em ícones direcionais.
title e decorativeUm elemento title, role="img" e aria-hiddenProps de acessibilidade no wrapper.
Tamanhos relativos"1em", "2rem"Somente números.

Props

Estas opções de renderToString e toSvg alteram o próprio desenho e funcionam no React Native. Elas têm os mesmos nomes e o mesmo significado das props de todos os outros pacotes do glyphfox.

OpçãoTipoPadrãoDescrição
sizenumber, string24Largura e altura; números são pixels.
colorstringcurrentColorCor principal.
secondaryOpacitynumber0.3 / 1Opacidade da camada duotone ou do preenchimento sólido.
weightnumber2Traço de 1 a 3 na grade de 24, com compensação óptica abaixo de 24 px.
absoluteWeightbooleanfalseMantém o traço com weight pixels em qualquer tamanho.
cornersround, sharproundJunções e pontas redondas ou retas.
variantoutline, duotone, solidoutlineEixo de estilo; o sólido vem das formas fechadas.
rotatenumber0Rotação em graus.
flipXbooleanfalseEspelha na horizontal.
flipYbooleanfalseEspelha na vertical.
modifierplus, minus, check, x, alert, lock, clock, sync, arrow-up, arrow-down, dotnenhumSelo com espaço de recorte automático.
modifierCornertop-left, top-right, bottom-left, bottom-rightbottom-rightCanto do selo.

Armadilhas comuns

Usar glyphfox/react
Os componentes para web renderizam elementos do DOM, então o React Native para com um erro sobre a falta de view config para svg. Renderize com o SvgXml em vez disso.
O conjunto inteiro no bundle
import { toSvg } from 'glyphfox' leva todos os ícones para o app, porque o Metro mantém tudo. Importe os dados dos ícones um a um, ou gere um pacote de subconjunto.
Erro "Unable to resolve" em glyphfox/core
Antes do React Native 0.79, os package exports vêm desativados no Metro. Ative unstable_enablePackageExports.
Parse a cada renderização
Uma chamada inline a renderToString em uma lista longa gera e faz o parse de uma string por linha e por renderização. Memoize a string, como faz o componente Glyph, ou use strings pré-geradas.
Nomes desconhecidos
toSvg lança um erro para um nome que não existe. Valide os nomes que vêm de um servidor com iconNames, ou mapeie você mesmo esses nomes para os dados importados.

Perguntas frequentes

Funciona com Expo?

Sim. Instale o react-native-svg com npx expo install react-native-svg e use o mesmo componente Glyph. O Expo usa o Metro, então a observação sobre package exports vale aqui: SDKs baseados no React Native 0.79 ou mais recente não precisam de nenhuma mudança.

Como faço um spinner de carregamento?

Desenhe loader ou loader-circle com o componente Glyph e gire uma Animated.View em volta dele com uma animação timing em loop. O native driver mantém tudo fluido, e a string SVG é gerada uma única vez.

Ícones deste guia