Optimización de Fuentes
Auto-aloja y optimiza fuentes web con next/font -- sin desplazamiento de diseño, sin solicitudes de red externas, subsetting automático.
Receta
Tarjeta de receta de referencia rápida -- lista para copiar y pegar.
// app/layout.tsx
import { Inter } from "next/font/google";
const inter = Inter({
subsets: ["latin"],
display: "swap",
});
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={inter.className}>
<body>{children}</body>
</html>
);
}// Fuente variable con variable CSS
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
});
// Usa en Tailwind CSS
<html className={inter.variable}>
// tailwind.config.ts: fontFamily: { sans: ["var(--font-inter)"] }Cuándo usarlo: Cada proyecto de Next.js que usa fuentes personalizadas. next/font elimina FOUT (flash of unstyled text), auto-aloja los archivos de fuente, y requiere cero solicitudes de red externas.
Ejemplo de Funcionamiento
// app/layout.tsx -- múltiples fuentes con variables CSS
import { Inter, JetBrains_Mono } from "next/font/google";
const inter = Inter({
subsets: ["latin"],
variable: "--font-sans",
display: "swap",
});
const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
variable: "--font-mono",
display: "swap",
});
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className={`${inter.variable} ${jetbrainsMono.variable}`}>
<body className="font-sans antialiased">{children}</body>
</html>
);
}// tailwind.config.ts
import type { Config } from "tailwindcss";
const config: Config = {
theme: {
extend: {
fontFamily: {
sans: ["var(--font-sans)", "system-ui", "sans-serif"],
mono: ["var(--font-mono)", "Menlo", "monospace"],
},
},
},
};
export default config;// app/components/code-block.tsx
export function CodeBlock({ code }: { code: string }) {
return (
<pre className="font-mono bg-gray-900 text-green-400 p-4 rounded overflow-x-auto">
<code>{code}</code>
</pre>
);
}// Usando una fuente personalizada local
// app/fonts.ts
import localFont from "next/font/local";
export const calSans = localFont({
src: [
{
path: "../public/fonts/CalSans-Regular.woff2",
weight: "400",
style: "normal",
},
{
path: "../public/fonts/CalSans-Bold.woff2",
weight: "700",
style: "normal",
},
],
variable: "--font-cal",
display: "swap",
});// app/layout.tsx -- combinando fuentes de Google y locales
import { Inter } from "next/font/google";
import { calSans } from "./fonts";
const inter = Inter({ subsets: ["latin"], variable: "--font-sans" });
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className={`${inter.variable} ${calSans.variable}`}>
<body className="font-sans">
{children}
</body>
</html>
);
}Lo que esto demuestra:
- Cargando múltiples fuentes de Google con variables CSS
- Integrando variables de fuente con Tailwind CSS
- Cargando una fuente personalizada local con múltiples pesos
- Combinando fuentes de Google y locales en el mismo layout
display: "swap"para carga accesible de fuentes
Análisis Profundo
Cómo Funciona
next/fontdescarga archivos de fuente en tiempo de compilación y los auto-aloja junto a tus assets estáticos. El navegador no realiza solicitudes a Google Fonts (ni a ningún CDN externo) en tiempo de ejecución.- Los archivos de fuente se sirven con
Cache-Control: public, immutable, max-age=31536000para almacenamiento en caché óptimo. next/fontgenera automáticamente declaraciones@font-facecon los valores correctos defont-display,unicode-range, ysrc.- Cuando usas la opción
variable, se establece una propiedad personalizada CSS (por ejemplo,--font-sans) en el elemento donde aplicas la clase. Esto se integra limpiamente con la configuraciónfontFamilyde Tailwind CSS. - La opción
subsetsrestringe qué rangos de caracteres se incluyen, reduciendo el tamaño del archivo. Para idiomas de escritura latina,["latin"]suele ser suficiente. - Las fuentes variables incluyen todos los pesos y estilos en un archivo único, reduciendo el número de solicitudes de red en comparación con archivos de fuente estática.
Variaciones
Pesos de fuente específicos (fuentes no variables):
import { Roboto } from "next/font/google";
const roboto = Roboto({
weight: ["400", "500", "700"],
subsets: ["latin"],
display: "swap",
});Fuente con múltiples subsets:
const notoSans = Noto_Sans({
subsets: ["latin", "latin-ext", "cyrillic"],
display: "swap",
});Precarga de una fuente específica:
// next/font precarga automáticamente las fuentes usadas en el layout raíz.
// Para fuentes usadas solo en páginas específicas, impórtalas en el layout de esa página.
// app/blog/layout.tsx
import { Merriweather } from "next/font/google";
const merriweather = Merriweather({
weight: ["400", "700"],
subsets: ["latin"],
variable: "--font-serif",
});
export default function BlogLayout({ children }: { children: React.ReactNode }) {
return <div className={merriweather.variable}>{children}</div>;
}Métricas de fallback de fuente (size-adjust):
// next/font calcula automáticamente size-adjust, ascent-override,
// descent-override, y line-gap-override para la fuente de fallback para
// minimizar CLS. No se necesita configuración manual.
const inter = Inter({
subsets: ["latin"],
display: "swap",
adjustFontFallback: true, // default -- genera métricas de fallback coincidentes
});Notas de TypeScript
import type { NextFont } from "next/dist/compiled/@next/font";
// Las funciones next/font devuelven un objeto NextFont
const inter: NextFont = Inter({ subsets: ["latin"] });
// Propiedades clave
inter.className; // string -- clase CSS que aplica la fuente
inter.variable; // string | undefined -- clase de variable CSS (si se usa la opción variable)
inter.style; // { fontFamily: string; fontWeight?: number; fontStyle?: string }
// Tipo de fuente local source
import localFont from "next/font/local";
const myFont = localFont({
src: [
{ path: "./font-regular.woff2", weight: "400", style: "normal" },
{ path: "./font-bold.woff2", weight: "700", style: "normal" },
{ path: "./font-italic.woff2", weight: "400", style: "italic" },
],
});Gotchas
-
Fuente no aplicándose -- Olvidar agregar la clase
classNameovariablea un elemento padre significa que la fuente nunca se activa. Solución: Aplicainter.className(ointer.variable) a<html>,<body>, o el contenedor relevante. -
Fuentes variables con pesos explícitos -- Algunas fuentes de Google Fonts están disponibles como fuentes variables pero pasas pesos específicos de todos modos, causando que Next.js descargue archivos de fuente estática separados. Solución: Omite la opción
weightpara fuentes variables para obtener el archivo de fuente variable único. -
Múltiples subsets aumentan el tamaño del bundle -- Cada subset se suma al tamaño del archivo de fuente. Solución: Solo incluye los subsets que tu contenido realmente usa.
-
display: "optional"puede causar texto invisible -- Confont-display: optional, el navegador puede saltarse la fuente personalizada completamente si no carga lo suficientemente rápido (dentro de ~100ms). Solución: Usadisplay: "swap"para la mayoría de casos para garantizar que la fuente personalizada eventualmente se muestre. -
Parpadeo de fuente en navegación cliente -- Si una fuente se carga en un layout específico de página (no el layout raíz), puede parpadear durante la navegación del lado del cliente a esa página. Solución: Carga las fuentes de uso frecuente en el layout raíz para que siempre estén disponibles.
-
next/font solo funciona en Server Components -- No puedes llamar a
Inter()olocalFont()dentro de un archivo"use client". Solución: Define fuentes en un Server Component (comolayout.tsxo un archivo dedicadofonts.ts) y aplica las clases mediante el DOM.
Alternativas
| Alternativa | Usa Cuando | No Uses Cuando |
|---|---|---|
next/font/google | Quieres fuentes de Google auto-alojadas con cero configuración | Usas una fuente propietaria no en Google Fonts |
next/font/local | Tienes archivos de fuente personalizados (woff2, ttf, otf) | La fuente está disponible en Google Fonts |
CDN de Google Fonts (<link>) | Nunca en Next.js -- next/font es estrictamente mejor | -- |
| Stack de fuentes del sistema | Quieres máximo rendimiento con cero descargas de fuente | Las directrices de marca requieren un typeface específico |
| Fontsource (paquetes npm) | Quieres paquetes de fuentes gestionados por npm fuera de Next.js | next/font ya maneja auto-alojamiento |
Preguntas Frecuentes
¿Qué hace next/font y por qué debería usarlo?
- Descarga archivos de fuente en tiempo de compilación y los auto-aloja como assets estáticos.
- Elimina solicitudes externas a Google Fonts en tiempo de ejecución.
- Evita desplazamiento de diseño (CLS) con ajuste automático de métricas de fuente fallback.
- Genera automáticamente declaraciones
@font-face.
¿Cómo uso una fuente de Google con Tailwind CSS?
Usa la opción variable para crear una propiedad personalizada CSS, luego refiérala en tailwind.config.ts:
const inter = Inter({ subsets: ["latin"], variable: "--font-sans" });
// tailwind.config.ts
fontFamily: { sans: ["var(--font-sans)", "system-ui"] }Aplica inter.variable como clase en <html>.
¿Cómo cargo una fuente personalizada local?
import localFont from "next/font/local";
const myFont = localFont({
src: [
{ path: "./font-regular.woff2", weight: "400" },
{ path: "./font-bold.woff2", weight: "700" },
],
variable: "--font-custom",
});¿Cuál es la diferencia entre className y variable en un objeto de fuente?
classNameaplica la fuente directamente mediante una clase CSS con una reglafont-family.variableestablece una propiedad personalizada CSS (por ejemplo,--font-sans) que puedes referenciar en CSS o en la configuración de Tailwind. Usavariablecuando integres con Tailwind.
Gotcha: Mi fuente no se aplica aunque la importé. ¿Qué está mal?
Probablemente olvidaste agregar inter.className o inter.variable como clase en un elemento padre (por ejemplo, <html> o <body>). La fuente no se activa sin que la clase se aplique al DOM.
Gotcha: Especifiqué pesos para una fuente variable y ahora mi bundle es más grande. ¿Por qué?
Pasar valores de weight explícitos para una fuente que soporta pesos variables causa que Next.js descargue archivos de fuente estática separados en lugar de un archivo de fuente variable único. Omite la opción weight para obtener el archivo de fuente variable único.
¿Puedo usar next/font dentro de un Client Component?
No. Las funciones next/font (Inter(), localFont()) solo se pueden llamar en Server Components. Define fuentes en layout.tsx o un archivo dedicado fonts.ts (sin "use client") y aplica las clases mediante el DOM.
¿Qué hace display: "swap" versus display: "optional"?
"swap": muestra una fuente fallback inmediatamente, luego cambia a la fuente personalizada una vez cargada. Garantiza que la fuente personalizada eventualmente aparezca."optional": el navegador puede saltarse la fuente personalizada completamente si no carga dentro de ~100ms. Puede causar texto personalizado invisible o faltante.
¿Cómo cierro el alcance de una fuente a una sección específica de la app?
Importa la fuente en un layout anidado en lugar del layout raíz:
// app/blog/layout.tsx
import { Merriweather } from "next/font/google";
const merriweather = Merriweather({ weight: ["400"], subsets: ["latin"], variable: "--font-serif" });
export default function BlogLayout({ children }) {
return <div className={merriweather.variable}>{children}</div>;
}¿Cuál es el tipo de TypeScript devuelto por las funciones next/font?
import type { NextFont } from "next/dist/compiled/@next/font";
const inter: NextFont = Inter({ subsets: ["latin"] });
// Propiedades: inter.className, inter.variable, inter.style¿Cómo escribo el array src para localFont en TypeScript?
Cada entrada es un objeto con path (string), weight (string), y opcionalmente style (string):
const myFont = localFont({
src: [
{ path: "./font-regular.woff2", weight: "400", style: "normal" },
{ path: "./font-italic.woff2", weight: "400", style: "italic" },
],
});¿Afecta next/font el rendimiento si cargo muchas fuentes?
Cada subset y archivo de fuente se suma a la salida de compilación. Solo incluye los subsets que tu contenido usa y limita el número de familias de fuente a lo que tu diseño requiere.
Relacionado
- Optimización de Imágenes -- Optimizando imágenes junto a fuentes
- Server Components -- Donde vive la configuración de fuentes
- Estático vs Dinámico -- Las fuentes son parte del shell estático
- Patrones de Composición -- Aplicando clases de fuente en layouts