Temas

Renderizado en el servidor

Los iconos de glyphfox son SVG puro, así que se renderizan en el servidor como cualquier otro marcado. Los detalles que importan son de dónde salen los datos de los iconos, qué ocurre durante la hidratación y qué formas de renderizado necesitan JavaScript en el navegador.

Qué renderiza cada paquete en el servidor

Todos los paquetes usan el mismo renderizador, así que un icono se ve idéntico tanto si lo renderiza React en un servidor como Vue en el navegador o toSvg en un script de compilación. Lo que cambia es si el servidor es capaz de generar el marcado.

Forma de renderizadoSalida en el servidorJavaScript en el navegador
Componentes de React, Vue, Svelte y Angularmarcado svg completosolo para morphs y para disparadores de clic y de estado
toSvg y renderToStringuna cadena svg completaninguno
El elemento glyph-foxun elemento vacíonecesario: el icono aparece cuando el elemento se inicializa
El runtime de data-glyphun <i> vacíonecesario: el runtime lo reemplaza
Clases de Tailwind CSSun elemento vacío con los estilos de tu CSSninguno
Sprite SVGun svg con una referencia useninguno

Los detalles de cada framework están en sus propias guías: el límite de cliente del App Router de Next.js, los plugins en Nuxt, los layouts en SvelteKit, los iconos sin JavaScript en Astro y Qwik, y SolidStart. Esta página trata lo que tienen en común.

Datos de los iconos en el servidor

Un componente como HomeIcon lleva sus propios datos y los registra cuando se ejecuta su módulo, así que siempre se renderiza en el servidor. En cambio, todo lo que hace referencia a un icono por su nombre busca ese nombre en un registro: Icon name, morphTo, las partes de Fusion, MorphIcon, useMorph, morphStore y el input name del componente de Angular.

Un nombre que no está registrado se carga bajo demanda con una importación dinámica. Esa carga solo empieza en el navegador, después del montaje, así que el servidor renderiza un marcador de posición del tamaño correcto y nada más:

server output
<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>

Registra los nombres que usas en un módulo que importen tanto el punto de entrada del servidor como el del cliente. Las exportaciones de datos, como iconSettings, son pequeñas y admiten tree shaking igual que los componentes.

icons.js
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]);

En Angular, la misma lista se pasa a provideGlyphFox({ icons }), que se ejecuta en el servidor y en el navegador; consulta la guía de Angular.

Cuando el servidor sabe más que el cliente

El problema contrario es igual de real. Cuando el servidor ejecuta el paquete sin pasarlo por un bundler, como hace Node con las dependencias que se mantienen externas, importar cualquier cosa de glyphfox/react o glyphfox/vue evalúa todo el punto de entrada, y cada uno de los 419 módulos de iconos se registra. Entonces <Icon name="settings" /> se renderiza por completo en el servidor.

En el bundle del navegador se aplica tree shaking, así que ahí settings no está registrado. El primer renderizado en el cliente produce el marcador de posición vacío, el marcado deja de coincidir con el HTML del servidor y el framework informa de una discrepancia de hidratación antes de que el icono se cargue de nuevo. Registrar los nombres de forma explícita, como se indica arriba, hace que ambos lados coincidan.

Cadenas SVG con toSvg

Fuera de un framework de componentes, toSvg(spec, props) devuelve el marcado como cadena. Acepta la sintaxis de nombres completa y todas las props compartidas, no depende del navegador y no realiza operaciones de E/S, así que funciona en plantillas de servidor, generadores de sitios estáticos, funciones serverless y scripts de compilación.

server.js
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);

El renderizado es rápido, pero los modificadores, las fusiones y el estilo sólido hacen un trabajo geométrico real. Para los iconos que se repiten en cada petición, guarda las cadenas en caché; la salida solo depende de la especificación y de las props.

icon.js
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 del punto de entrada principal carga todos los iconos, lo cual no es problema en un proceso de servidor. Donde importa el tamaño del código, como en las funciones edge con límite de tamaño, usa renderToString de glyphfox/core con los datos de cada icono desde glyphfox/icons/NAME; la guía de tree shaking tiene las cifras.

edge.js
import { renderToString } from 'glyphfox/core';
import download from 'glyphfox/icons/download';

export const downloadIcon = renderToString(download, { size: 18 });

Hidratación, morphs y estados vivos

El servidor renderiza el estado que tiene el icono en el momento del renderizado. Los morphs se calculan en el navegador: MorphIcon, morphTo y los hooks empiezan a animar después de la hidratación, porque los efectos no se ejecutan en el servidor. Un icono con morphTo, trigger="state" y active ya en true renderiza su destino en el servidor, así que un menú que empieza abierto muestra la x en el HTML.

Los estados vivos no necesitan hidratación en absoluto. Su CSS está dentro del SVG, así que una animación mount se reproduce en cuanto el navegador analiza el HTML y una animación hover funciona antes de que se cargue ningún script. Una animación state que ya está activa se renderiza con su clase gf-on y también se reproduce desde el HTML. Los clics y los cambios de estado posteriores necesitan el framework, porque es el que alterna el atributo y la clase que ponen en marcha la animación. La regla prefers-reduced-motion viaja en la misma hoja de estilos y también se aplica al marcado del servidor.

El marcado es determinista. Las clases de animación, los ids de máscara y las clases de morph son hashes de su contenido, no contadores ni valores aleatorios, así que el servidor y el navegador producen los mismos atributos y los ids nunca provocan una discrepancia de hidratación.

El web component en páginas renderizadas en el servidor

El elemento glyph-fox se dibuja en un shadow root cuando se ejecuta su script. No usa shadow DOM declarativo, así que el HTML del servidor contiene un elemento vacío y sin tamaño, y la línea que lo rodea se desplaza cuando aparece el icono. Reserva el espacio hasta que el elemento esté definido:

app.css
glyph-fox:not(:defined) {
  display: inline-block;
  width: 24px;
  height: 24px;
  vertical-align: middle;
}

Para los iconos que se ven sin hacer scroll, renderiza SVG en línea en el servidor con un componente o con toSvg, y reserva el elemento para las partes de la página que cambian en el cliente. La guía de web components explica el elemento en detalle.

Correos electrónicos

El renderizado en el servidor a menudo acaba en una plantilla de correo electrónico, y ese es el único lugar en el que las cadenas SVG no sirven: muchos clientes de correo no muestran SVG en línea ni imágenes SVG. Usa archivos PNG en ese caso. El explorador de iconos permite descargar cualquier icono como PNG al tamaño que necesites, con tu color y tu peso.

Opciones de toSvg

toSvg y renderToString aceptan las mismas props que los componentes, como opciones normales.

OpciónTipoPor defectoDescripción
sizenumber, string24Ancho y alto; los números son píxeles.
colorstringcurrentColorColor principal.
secondaryColorstringcolorColor de la capa duotone y del relleno sólido.
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.
mirrorRtlbooleanfalseRefleja en layouts de derecha a izquierda.
animatedraw, pop, wiggle, spin, pulse, bounce, shakeningunoPreset de estado vivo.
triggermount, hover, click, statemountCuándo se reproduce la animación o el morph.
activebooleanfalseEl estado para el disparador state.
durationnumber (ms)según el presetDuración de la animación o del morph.
delaynumber (ms)0Retraso de la animación o del morph.
loopboolean, numberfalseRepite sin fin o un número de veces (spin y pulse se repiten por defecto).
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.
morphToname, dataningunoDestino del morph; el disparador decide cuándo se muestra.
titlestringningunoNombre accesible: title, role img y aria-label.
decorativebooleantrue sin titleOculta el icono a las tecnologías de apoyo.

Errores comunes

Iconos vacíos en el HTML del servidor
Un icono renderizado por nombre que nunca se registró es, en el servidor, un svg vacío con la clase glyphfox-empty. Registra los nombres o importa el componente de ese icono.
Avisos de discrepancia de hidratación
Si el servidor renderiza un nombre que el navegador todavía no tiene, el marcado difiere en el primer renderizado en el cliente. Las causas habituales son importar en el servidor un punto de entrada completo sin bundler y los nombres que vienen de datos. Registra los nombres en código compartido.
Iconos que dependen del navegador
Un icono elegido a partir de localStorage, del esquema de color o del tamaño de la ventana no puede coincidir en el servidor. Renderiza un icono neutro en el servidor y cámbialo después del montaje, o lee el valor de una cookie que el servidor pueda ver.
Desplazamientos del diseño causados por el elemento
Un glyph-fox sin definir no tiene tamaño. Reserva el espacio con :not(:defined) o renderiza SVG en línea en el servidor.
Destinos de morph en builds de producción
En un build de producción de Vite, los nombres que no se importan ni se registran se quedan vacíos en el navegador, porque el cargador bajo demanda que hay dentro de node_modules no se transforma. El renderizado en el servidor no lo oculta; registrar los nombres lo soluciona en ambos lados.

Preguntas frecuentes

¿Funcionan los componentes de React en React Server Components?

No directamente: usan hooks, así que necesitan un límite de cliente. En un Server Component puedes renderizar la salida de toSvg o renderToString con dangerouslySetInnerHTML sin enviar JavaScript para el icono. La guía de Next.js muestra ambas opciones.

¿Por qué mi icono está vacío en el HTML del servidor?

Porque se renderiza por nombre, a través de Icon, morphTo, Fusion o MorphIcon, y sus datos no estaban registrados cuando el servidor lo renderizó. Llama a registerIcons con las exportaciones de datos en un módulo que importen ambos puntos de entrada.

Iconos de esta guía