Frameworks

Iconos en Preact

Preact ejecuta los componentes de React de glyphfox mediante preact/compat sin cambiar el código. Si tu aplicación no carga compat, el web component glyph-fox funciona con Preact puro.

Instalación

Instala glyphfox junto a Preact. preact/compat forma parte del paquete preact, así que no hay que agregar una segunda dependencia.

terminal
npm install glyphfox

Tienes dos formas de renderizar iconos, y ambas comparten el mismo motor de renderizado y las mismas props:

  • Los componentes de glyphfox/react, que Preact ejecuta mediante compat. Es la mejor opción si tu aplicación ya define un alias para React o usa otros paquetes de React.
  • El web component <glyph-fox>, que no necesita compat en absoluto. Es la mejor opción si quieres mantener Preact ligero y solo necesitas iconos.

Cómo funcionan los componentes en Preact

El punto de entrada de React importa exactamente seis cosas de react: createElement, forwardRef, useState, useEffect, useMemo y useRef. compat las implementa todas, así que, en cuanto el bundler resuelve react a preact/compat, los componentes se renderizan con el propio renderizador de Preact, en el mismo árbol y con los mismos hooks que el resto de tu aplicación.

Los atributos SVG se crean con los nombres de React, como strokeWidth y strokeLinecap, y compat los vuelve a convertir en stroke-width y stroke-linecap en el elemento. Las animaciones de estados vivos son un elemento <style> dentro del SVG, definido mediante dangerouslySetInnerHTML, que Preact también admite.

Vite

El preset oficial de Preact para Vite ya redirige react, react-dom y el runtime de JSX a compat mediante alias. No hace falta nada más:

vite.config.js
import { defineConfig } from 'vite';
import preact from '@preact/preset-vite';

export default defineConfig({
  plugins: [preact()],
});

webpack y esbuild

Con webpack, agrega los alias a resolve.alias. glyphfox solo necesita react, pero crear alias para todo el conjunto mantiene cualquier otro paquete de React de tu aplicación en la misma instancia de Preact:

webpack.config.js
module.exports = {
  resolve: {
    alias: {
      react: 'preact/compat',
      'react-dom/test-utils': 'preact/test-utils',
      'react-dom': 'preact/compat',
      'react/jsx-runtime': 'preact/jsx-runtime',
    },
  },
};

esbuild tiene la misma opción. Activa a la vez la división de código; si no, esbuild incrusta todos los módulos de iconos a los que puede llegar el cargador bajo demanda (la guía de tree shaking explica por qué):

build.mjs
import * as esbuild from 'esbuild';

await esbuild.build({
  entryPoints: ['src/main.jsx'],
  bundle: true,
  splitting: true,
  format: 'esm',
  outdir: 'dist',
  jsx: 'automatic',
  jsxImportSource: 'preact',
  alias: { react: 'preact/compat', 'react-dom': 'preact/compat' },
});

TypeScript

Las declaraciones de glyphfox/react están escritas sobre los tipos de React (ForwardRefExoticComponent, SVGProps). Redirige react a compat en paths para que se resuelvan con los tipos de Preact. Si el verificador de tipos sigue mostrando problemas dentro de los archivos de declaración de las dependencias, deja activado skipLibCheck, como hace la mayoría de las plantillas de Preact:

tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact",
    "skipLibCheck": true,
    "paths": {
      "react": ["./node_modules/preact/compat/"],
      "react-dom": ["./node_modules/preact/compat/"]
    }
  }
}

Usa los componentes

Importa los iconos por su nombre y usa los hooks de Preact a su alrededor como siempre. Todo lo que explica la guía de React es aplicable: Icon con la sintaxis de nombres, Fusion, MorphIcon, useMorph, los modificadores y las refs.

Card.jsx
import { useState } from 'preact/hooks';
import { HeartIcon, MorphIcon, Fusion } from 'glyphfox/react';

export function Card() {
  const [liked, setLiked] = useState(false);
  return (
    <article className="card">
      <button type="button" onClick={() => setLiked(!liked)} aria-pressed={liked}>
        <HeartIcon animate="pop" trigger="state" active={liked} variant={liked ? 'solid' : 'outline'} />
        Like
      </button>
      <MorphIcon name={liked ? 'check' : 'plus'} duration={250} />
      <Fusion icons={['user', 'shield']} layout="badge" />
    </article>
  );
}

Las refs pasan por el forwardRef de compat, así que ref en un icono apunta al elemento <svg> renderizado, listo para getBoundingClientRect() o la API Web Animations. Los controladores de eventos como onClick y los atributos como data-* llegan directamente al elemento, exactamente igual que en React.

Con signals, pasa el valor y no la signal. Preact desenvuelve las signals enlazadas a atributos del DOM, pero un componente de icono recibe el propio objeto signal y lee sus props como valores simples, así que no leería el valor correcto ni se actualizaría cuando cambie la signal:

Status.jsx
import { signal } from '@preact/signals';
import { CloudIcon } from 'glyphfox/react';

const syncing = signal(false);

export function Status() {
  return <CloudIcon modifier={syncing.value ? 'sync' : undefined} />;
}

Sin compat: el web component

El custom element <glyph-fox> renderiza los mismos iconos con las mismas props, como atributos en kebab-case. Importa glyphfox/web una vez para definirlo con todos los iconos, o defínelo tú mismo y registra solo los iconos que usas:

icons.js
import { defineGlyphFox } from 'glyphfox/web/element';
import { registerIcons } from 'glyphfox/core';
import { iconHome, iconSearch, iconMenu, iconX } from 'glyphfox';

registerIcons([iconHome, iconSearch, iconMenu, iconX]);
defineGlyphFox();
Header.jsx
import { useState } from 'preact/hooks';
import './icons.js';

export function Header() {
  const [open, setOpen] = useState(false);
  return (
    <header>
      <glyph-fox name="home" size="20"></glyph-fox>
      <glyph-fox name="search" weight="1.5" title="Search"></glyph-fox>
      <button type="button" onClick={() => setOpen(!open)} aria-expanded={open} aria-label="Menu">
        <glyph-fox name={open ? 'x' : 'menu'} morph></glyph-fox>
      </button>
    </header>
  );
}

Preact asigna como atributos las props desconocidas de los custom elements, que es lo que escucha el elemento, así que los valores dinámicos como name={open ? 'x' : 'menu'} funcionan y el atributo morph anima cada cambio. En TypeScript, agrega glyph-fox a JSX.IntrinsicElements de Preact en un archivo de declaración, tal como describe la documentación de Preact para cualquier custom element.

Los atributos, el tamaño dentro del shadow DOM y el renderizado en el servidor del elemento se explican en la guía de web components.

Qué opción es más ligera

Las dos opciones incluyen el mismo motor (renderizado, ejes, modificadores, fusión y morph), así que los iconos cuestan más o menos lo mismo elijas la que elijas. La verdadera diferencia es compat: si tu aplicación ya lo carga para otros paquetes, los componentes no agregan nada nuevo; si no, el web component te lo ahorra. En ambos casos, importa solo los iconos que usas, como se muestra arriba.

Props

Los componentes aceptan estas props. En <glyph-fox>, las mismas props son atributos en kebab-case (secondary-color, flip-x, morph-to), además de name, layout, gap y morph.

PropTipoPor 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

Falta el alias
Sin el alias, el bundler resuelve react al paquete real de React (o no lo encuentra). Entonces los iconos llaman a hooks de otro renderizador, lo que se manifiesta como errores de llamada a hook no válida o iconos que nunca se actualizan. Comprueba que react se resuelve a preact/compat.
class en lugar de className
Los componentes combinan className con sus propias clases glyphfox glyphfox-NAME. Una prop class no se combina y esas clases pueden reemplazarla, así que usa className en los componentes de icono.
Dos copias de Preact
compat debe resolverse al mismo preact que usa tu aplicación. Una copia duplicada en node_modules rompe los hooks igual que la falta del alias; elimina el duplicado con tu gestor de paquetes.
Signals como props
Pasa signal.value a los componentes de icono. El objeto signal en sí no se desenvuelve en los componentes.
Elementos vacíos antes de la definición
<glyph-fox> solo se dibuja después de que defineGlyphFox() se haya ejecutado en el navegador. Con HTML prerenderizado, dale al elemento un tamaño en CSS para que el diseño no se desplace mientras carga el script.

Preguntas frecuentes

¿Necesito preact/compat para usar glyphfox?

Solo para los componentes de glyphfox/react. El elemento <glyph-fox> y toSvg de glyphfox funcionan en Preact puro sin él.

¿Los componentes se renderizan con preact-render-to-string?

Sí. Mediante compat se renderizan en el servidor como cualquier componente de React, y el SVG queda en el HTML. Los iconos renderizados por nombre con Icon necesitan tener antes sus datos registrados, como se describe en la guía de renderizado en el servidor.

Iconos de esta guía