La API de fuentes de Astro
El origen de la API de fuentes como experimental.fonts, su estabilización en Astro 7 y la declaración de familias en el arreglo fonts: provider, name y cssVariable como contrato, con proveedores locales y remotos que Astro descarga, optimiza y autoaloja por ti.
Durante años, añadir una fuente a la web fue una tarea artesanal y frágil: un <link> a un CDN, una regla @font-face copiada a mano, una carrera invisible entre el texto y sus glifos, y una fuga silenciosa de datos hacia un tercero. Astro 7 sustituye esa artesanía por una API de fuentes declarativa, tipada y unificada: describes qué familias quieres y de dónde salen, y el framework se encarga de descargarlas, optimizarlas, autoalojarlas y cachearlas. Esta lección presenta el gesto fundacional de esa API, declarar una familia, y el modelo mental que lo sostiene.
- Entender qué problema resuelve la API de fuentes y su origen como
experimental.fonts. - Declarar una familia en el arreglo
fontscon las tres claves obligatorias. - Distinguir un proveedor remoto de uno local y cuándo usar cada uno.
- Reconocer qué trabajo asume Astro por ti: descarga, optimización y autoalojamiento.
De la fontanería manual a una declaración
El método clásico para usar una fuente web mezclaba tres piezas dispersas: un enlace de preconexión al origen del CDN, una hoja de estilos remota que definía las reglas @font-face, y la esperanza de que todo llegara a tiempo. Escrito a mano, se veía así.
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter&display=swap" rel="stylesheet" />
Cada línea esconde un coste: un viaje a otro dominio, una dependencia de tiempo de ejecución, y ninguna garantía sobre qué pesos o subconjuntos acaban descargándose. Y aún faltaba lo peor. Mientras la fuente viajaba por la red, el navegador tenía que decidir qué mostrar: o bien ocultaba el texto hasta que llegara, dejando un hueco en blanco, o bien lo pintaba con una fuente de respaldo y luego lo reemplazaba de golpe, desplazando la maquetación. Esas dos patologías, el texto invisible y el salto de contenido, eran el precio tácito de cada fuente web, y corregirlas a mano exigía un conocimiento fino que casi nadie tenía tiempo de aplicar.
La API de fuentes de Astro invierte el planteamiento. En lugar de enlazar recursos ajenos en cada página, declaras las familias una vez en la configuración y dejas que el framework resuelva el resto en el build. La fuente deja de ser un enlace externo y pasa a ser un activo de tu proyecto, tan versionado y controlado como una imagen. Las patologías clásicas dejan de ser tu problema porque el framework las ataca por defecto, un tema que desmenuzaremos en las lecciones sobre el componente y el rendimiento.
Esta API nació tras la bandera experimental.fonts y maduró hasta estabilizarse; en Astro 7 ya no necesitas ningún flag: las familias se declaran en la opción fonts de nivel superior. El componente que verás en la próxima lección, <Font />, se exporta desde astro:assets. Bajo el capó, todo el sistema se apoya en unifont, una capa universal que sabe hablar con muchos catálogos de fuentes distintos.
El arreglo fonts: anatomía de una familia
La declaración vive en astro.config.mjs, en un arreglo fonts donde cada objeto describe una familia. Tres claves son obligatorias y bastan para empezar: el provider, que dice de dónde sale la fuente; el name, el nombre de la familia tal como lo conoce ese proveedor; y el cssVariable, el identificador con el que la consumirás desde el CSS.
---
// astro.config.mjs
import { defineConfig, fontProviders } from 'astro/config';
export default defineConfig({
fonts: [
{
provider: fontProviders.google(),
name: 'Inter',
cssVariable: '--font-inter',
},
],
});
El cssVariable es la pieza conceptualmente más importante: es el contrato entre la configuración y tus estilos. No escribes el nombre de la fuente en el CSS ni te preocupas por su pila de fallbacks; te refieres a ella por esa variable, y Astro garantiza que apunte a la familia correcta, con sus respaldos, allí donde la uses. La configuración y el diseño quedan acoplados solo por ese nombre, no por los detalles de descarga.
Conviene que el cssVariable describa el papel de la fuente, no su marca: --font-titulo o --font-cuerpo envejecen mejor que --font-inter, porque el día que cambies Inter por otra familia solo tocas la configuración y ni una línea de CSS. La variable es un rol tipográfico; el name es su encarnación actual.
Proveedores locales y remotos
El provider decide el origen. Un proveedor remoto como fontProviders.google() o fontProviders.fontsource() resuelve la familia contra un catálogo en la red y descarga sus archivos durante el build. Un proveedor local, fontProviders.local(), no busca nada fuera: toma archivos que ya tienes en el disco y los declara como variantes, cada una un @font-face con su src.
---
// astro.config.mjs
import { defineConfig, fontProviders } from 'astro/config';
export default defineConfig({
fonts: [
{
provider: fontProviders.local(),
name: 'Geist',
cssVariable: '--font-geist',
options: {
variants: [
{ weight: 400, style: 'normal', src: ['./src/assets/fonts/Geist-400.woff2'] },
{ weight: 700, style: 'normal', src: ['./src/assets/fonts/Geist-700.woff2'] },
],
},
},
],
});
La diferencia práctica es de procedencia, no de trato: una vez resuelta, Astro autoaloja ambas por igual. Da igual que la fuente viniera de Google o de tu carpeta src/assets; en la salida del build, todas se sirven desde tu propio origen con las mismas optimizaciones. El proveedor es solo la puerta por la que entra la fuente, no cómo se sirve después.
Sobre esas tres claves obligatorias se apilan otras opcionales que afinan qué se descarga y cómo se comporta: weights para elegir grosores, styles para normal o cursiva, subsets para acotar los rangos de caracteres, fallbacks para la pila de respaldo y display para la estrategia de intercambio. No las necesitas para empezar, y sus valores por defecto son sensatos, pero son las palancas con las que más adelante recortarás el peso y borrarás los saltos de maquetación.
---
// astro.config.mjs: la misma familia, ya refinada
export default defineConfig({
fonts: [
{
provider: fontProviders.google(),
name: 'Inter',
cssVariable: '--font-inter',
weights: [400, 600, 700],
styles: ['normal'],
subsets: ['latin', 'latin-ext'],
},
],
});
Por ahora basta con retener la forma esencial: una familia es un proveedor, un nombre y una variable, y todo lo demás es refinamiento que puedes añadir el día que lo necesites, sin reescribir nada de lo anterior.
Para el proveedor local, coloca los archivos en src (por ejemplo src/assets/fonts) y no en public. La razón es sutil pero importante: Astro copia las fuentes resueltas a la salida durante el build, así que si ya estuvieran en public acabarían duplicadas, una vez como recurso crudo sin optimizar y otra como activo procesado. Dejarlas en src las mantiene fuera del despliegue directo y bajo el control del pipeline, que es justo lo que quieres.
Proveedor remoto
google, fontsource, bunny, fontshare, adobe, npm. Resuelven y descargan la familia desde un catalogo en el build.
Proveedor local
local. Declara archivos de tu disco como variantes con src, sin salir a la red.
cssVariable
El contrato con el CSS. Te refieres a la fuente por su variable, no por su nombre ni su pila.
flowchart LR CFG[arreglo fonts en la config] --> PROV[provider elegido] PROV --> REM[remoto descarga del catalogo] PROV --> LOC[local lee del disco] REM --> RES[Astro resuelve y optimiza] LOC --> RES RES --> SELF[autoalojado en tu origen] SELF --> VAR[cssVariable disponible en el CSS] style CFG fill:#89b4fa,color:#11111b style VAR fill:#a6e3a1,color:#11111b
El salto profundo de esta API no es que escribas menos líneas, sino que cambia la naturaleza de lo que una fuente es dentro de tu proyecto. En el modelo antiguo, una fuente era una dependencia de tiempo de ejecución: un cable tendido desde el navegador de cada visitante hasta el servidor de un tercero, que se ejercitaba en cada visita y podía fallar, tardar o cambiar sin avisarte. Tú no poseías la fuente; poseías una referencia a ella. La API de fuentes convierte ese cable en un activo. Cuando declaras una familia, no estás pidiendo que se conecte a algo en cada carga; estás afirmando una intención, esta familia, con este proveedor, bajo este nombre, que Astro materializa una sola vez en el build y hornea dentro de tu salida. La fuente pasa a ser tan tuya, tan versionada y tan predecible como cualquier archivo de tu repositorio. Este desplazamiento, de la referencia externa al activo interno, es el mismo que Astro aplica a las imágenes y a los estilos, y responde a una convicción de fondo: el momento correcto para hacer el trabajo costoso e incierto, resolver, descargar, optimizar, es el build, donde puedes controlarlo y verificarlo, no el runtime, donde cada usuario lo paga de nuevo y cualquier cosa puede salir mal. Interiorizar esto reordena tus prioridades: dejas de preguntarte a qué URL enlazar y empiezas a preguntarte qué familias forman parte de tu sistema, confiando en que su procedencia sea un detalle que se resuelve una vez y desaparece.
- Añade el arreglo
fontsa tuastro.config.mjscon una familia defontProviders.google(), dándolenameycssVariable. - Cambia el
cssVariablea un nombre por rol, como--font-cuerpo, y observa que la configuración no depende de la marca de la fuente. - Añade una segunda familia con
fontProviders.local()y un par de variantes con distintos pesos apuntando a archivos de tu carpetasrc/assets/fonts. - Lanza el build y localiza dónde deja Astro los archivos de fuente resueltos; comprueba que ambas familias, la remota y la local, acaban servidas desde tu propio origen.