Renderização no servidor
Os ícones do glyphfox são SVG puro, então são renderizados no servidor como qualquer outra marcação. Os detalhes que importam são de onde vêm os dados dos ícones, o que acontece na hidratação e quais formas de renderização precisam de JavaScript no navegador.
O que cada pacote renderiza no servidor
Todos os pacotes usam o mesmo renderizador, então um ícone fica idêntico, seja renderizado pelo React em um servidor, pelo Vue no navegador ou pelo toSvg em um script de build. O que muda é se o servidor consegue ou não gerar a marcação.
| Forma de renderização | Saída do servidor | JavaScript no navegador |
|---|---|---|
| Componentes React, Vue, Svelte e Angular | marcação svg completa | só para morphs e para gatilhos de clique e de estado |
toSvg e renderToString | uma string svg completa | nenhum |
O elemento glyph-fox | um elemento vazio | obrigatório: o ícone aparece quando o elemento passa pelo upgrade |
O runtime data-glyph | um <i> vazio | obrigatório: o runtime o substitui |
| Classes do Tailwind CSS | um elemento vazio estilizado pelo seu CSS | nenhum |
| Sprite SVG | um svg com uma referência use | nenhum |
Os detalhes de cada framework estão nos guias específicos: a fronteira de cliente do App Router do Next.js, plugins no Nuxt, layouts no SvelteKit, ícones sem JavaScript no Astro e no Qwik, e o SolidStart. Esta página trata do que eles têm em comum.
Dados dos ícones no servidor
Um componente como HomeIcon carrega os próprios dados e os registra quando o seu módulo é executado, então sempre é renderizado no servidor. Já tudo o que se refere a um ícone pelo nome procura esse nome em um registro: Icon name, morphTo, as partes de Fusion, MorphIcon, useMorph, morphStore e o input name do componente Angular.
Um nome que não está registrado é carregado sob demanda com um import dinâmico. Esse carregamento só começa no navegador, depois da montagem, então o servidor renderiza um placeholder do tamanho certo e mais nada:
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="glyphfox glyphfox-empty" aria-hidden="true"></svg>
Registre os nomes que você usa em um módulo importado tanto pela entrada do servidor quanto pela entrada do cliente. Os exports de dados, como iconSettings, são pequenos e passam por tree shaking assim como os componentes.
import { registerIcons } from 'glyphfox/react'; // or glyphfox/vue, glyphfox/svelte import { iconSettings, iconFile, iconCloud, iconLock, iconX } from 'glyphfox'; // Names used through Icon, morphTo, Fusion and MorphIcon. registerIcons([iconSettings, iconFile, iconCloud, iconLock, iconX]);
No Angular, a mesma lista vai para provideGlyphFox({ icons }), que roda no servidor e no navegador; veja o guia de Angular.
Quando o servidor sabe mais que o cliente
O problema oposto é igualmente real. Quando o servidor executa o pacote sem passar por um bundler, como o Node faz com dependências que ficam externas, importar qualquer coisa de glyphfox/react ou glyphfox/vue avalia o ponto de entrada inteiro, e cada um dos 419 módulos de ícone se registra. Com isso, <Icon name="settings" /> é renderizado por completo no servidor.
O bundle do navegador passa por tree shaking, então settings não está registrado lá. A primeira renderização no cliente produz o placeholder vazio, a marcação deixa de bater com o HTML do servidor, e o framework acusa uma divergência de hidratação (hydration mismatch) antes de o ícone carregar de novo. Registrar os nomes explicitamente, como mostrado acima, faz os dois lados concordarem.
Strings SVG com toSvg
Fora de um framework de componentes, toSvg(spec, props) retorna a marcação como string. Ele aceita a sintaxe de nomes completa e todas as props compartilhadas, não depende do navegador e não faz I/O, então funciona em templates de servidor, geradores de sites estáticos, funções serverless e scripts de build.
import express from 'express'; import { toSvg } from 'glyphfox'; const app = express(); app.get('/', (req, res) => { res.send(`<!doctype html> <title>Files</title> <ul> <li>${toSvg('file+check', { size: 18 })} report.pdf</li> <li>${toSvg('cloud&lock:overlap', { size: 18, title: 'Encrypted backup' })} backup.zip</li> </ul>`); }); app.listen(3000);
A renderização é rápida, mas modificadores, fusões e o estilo sólido fazem cálculos geométricos de verdade. Para ícones que se repetem em toda requisição, guarde as strings em cache; a saída depende só da spec e das props.
import { toSvg } from 'glyphfox'; const cache = new Map(); export function icon(spec, props = {}) { const key = spec + JSON.stringify(props); let svg = cache.get(key); if (svg === undefined) { svg = toSvg(spec, props); cache.set(key, svg); } return svg; }
toSvg do ponto de entrada principal carrega todos os ícones, o que não é problema para um processo de servidor. Onde o tamanho do código importa, como em edge functions com limite de tamanho, use renderToString de glyphfox/core com os dados de cada ícone vindos de glyphfox/icons/NAME; o guia de tree shaking traz os números.
import { renderToString } from 'glyphfox/core'; import download from 'glyphfox/icons/download'; export const downloadIcon = renderToString(download, { size: 18 });
Hidratação, morphs e estados vivos
O servidor renderiza o estado que o ícone tem no momento da renderização. Os morphs são calculados no navegador: MorphIcon, morphTo e os hooks começam a animar depois da hidratação, porque efeitos não rodam no servidor. Um ícone com morphTo, trigger="state" e active já verdadeiro renderiza o destino no servidor, então um menu que começa aberto mostra o x no HTML.
Os estados vivos não precisam de hidratação nenhuma. O CSS deles fica dentro do SVG, então uma animação mount toca assim que o navegador analisa o HTML, e uma animação hover funciona antes de qualquer script carregar. Uma animação state que já está ativa é renderizada com a classe gf-on e também toca direto do HTML. Cliques e mudanças de estado posteriores precisam do framework, porque é ele que alterna o atributo e a classe que disparam essas animações. A regra prefers-reduced-motion vai na mesma folha de estilo e também vale para a marcação gerada no servidor.
A marcação é determinística. Classes de animação, ids de máscara e classes de morph são hashes do próprio conteúdo, e não contadores ou valores aleatórios, então o servidor e o navegador produzem os mesmos atributos, e os ids nunca causam divergência de hidratação.
O web component em páginas renderizadas no servidor
O elemento glyph-fox desenha dentro de um shadow root quando o script dele roda. Ele não usa declarative shadow DOM, então o HTML do servidor contém um elemento vazio e sem tamanho, e a linha em volta se mexe quando o ícone aparece. Reserve o espaço até o elemento ser definido:
glyph-fox:not(:defined) { display: inline-block; width: 24px; height: 24px; vertical-align: middle; }
Para ícones acima da dobra, renderize SVG inline no servidor com um componente ou com toSvg e deixe o elemento para as partes da página que mudam no cliente. O guia de web components cobre o elemento em detalhes.
E-mails
A renderização no servidor muitas vezes termina em um template de e-mail, e esse é o único lugar onde strings SVG não ajudam: muitos clientes de e-mail não exibem SVG inline nem imagens SVG. Nesse caso, use arquivos PNG. O explorador de ícones baixa qualquer ícone como PNG no tamanho de que você precisar, com a sua cor e o seu peso.
Opções do toSvg
toSvg e renderToString recebem as mesmas props dos componentes, como opções simples.
| Opção | Tipo | Padrão | Descrição |
|---|---|---|---|
size | number, string | 24 | Largura e altura; números são pixels. |
color | string | currentColor | Cor principal. |
secondaryColor | string | color | Cor da camada duotone e do preenchimento sólido. |
secondaryOpacity | number | 0.3 / 1 | Opacidade da camada duotone ou do preenchimento sólido. |
weight | number | 2 | Traço de 1 a 3 na grade de 24, com compensação óptica abaixo de 24 px. |
absoluteWeight | boolean | false | Mantém o traço com weight pixels em qualquer tamanho. |
corners | round, sharp | round | Junções e pontas redondas ou retas. |
variant | outline, duotone, solid | outline | Eixo de estilo; o sólido vem das formas fechadas. |
rotate | number | 0 | Rotação em graus. |
flipX | boolean | false | Espelha na horizontal. |
flipY | boolean | false | Espelha na vertical. |
mirrorRtl | boolean | false | Espelha em layouts da direita para a esquerda. |
animate | draw, pop, wiggle, spin, pulse, bounce, shake | nenhum | Animação de estado vivo. |
trigger | mount, hover, click, state | mount | Quando a animação ou o morph acontece. |
active | boolean | false | O estado para o gatilho state. |
duration | number (ms) | por animação | Duração da animação ou do morph. |
delay | number (ms) | 0 | Atraso da animação ou do morph. |
loop | boolean, number | false | Repete para sempre ou um número de vezes (spin e pulse repetem por padrão). |
modifier | plus, minus, check, x, alert, lock, clock, sync, arrow-up, arrow-down, dot | nenhum | Selo com espaço de recorte automático. |
modifierCorner | top-left, top-right, bottom-left, bottom-right | bottom-right | Canto do selo. |
morphTo | name, data | nenhum | Destino do morph; o gatilho decide quando aparece. |
title | string | nenhum | Nome acessível: title, role img e aria-label. |
decorative | boolean | true sem title | Esconde o ícone das tecnologias assistivas. |
Armadilhas comuns
- Ícones vazios no HTML do servidor
- Um ícone renderizado pelo nome que nunca foi registrado vira, no servidor, um
svgvazio com a classeglyphfox-empty. Registre os nomes ou importe o componente desse ícone. - Avisos de divergência de hidratação
- Se o servidor renderiza um nome que o navegador ainda não tem, a marcação fica diferente na primeira renderização no cliente. As causas mais comuns são um import no servidor, sem bundler, de um ponto de entrada inteiro e nomes que vêm de dados. Registre os nomes em código compartilhado.
- Ícones que dependem do navegador
- Um ícone escolhido a partir do
localStorage, do esquema de cores ou do tamanho da janela não tem como coincidir no servidor. Renderize um ícone neutro no servidor e troque depois da montagem, ou leia o valor de um cookie que o servidor consiga ver. - Deslocamento de layout causado pelo elemento
- Um
glyph-foxainda não definido não tem tamanho. Reserve o espaço com:not(:defined)ou renderize SVG inline no servidor. - Destinos de morph em builds de produção
- Em um build de produção do Vite, nomes que não foram importados nem registrados ficam vazios no navegador, porque o carregador sob demanda dentro de
node_modulesnão é transformado. A renderização no servidor não esconde isso; registrar os nomes resolve os dois lados.
Perguntas frequentes
Os componentes React funcionam em React Server Components?
Não diretamente: eles usam hooks, então precisam de uma fronteira de cliente. Em um Server Component, você pode renderizar a saída de toSvg ou renderToString com dangerouslySetInnerHTML e não enviar nenhum JavaScript para o ícone. O guia de Next.js mostra as duas abordagens.
Por que o meu ícone fica vazio no HTML do servidor?
Ele é renderizado pelo nome, via Icon, morphTo, Fusion ou MorphIcon, e os dados dele não estavam registrados quando o servidor renderizou. Chame registerIcons com os exports de dados em um módulo importado pelas duas entradas.