Has agregado favicon.ico a tu directorio app y funciona. Pero, ¿qué pasa cuando necesitas iconos diferentes para páginas de marketing frente al dashboard, o cuando el icono debe reflejar la preferencia de tema del usuario sin una recarga completa de la página? La convención basada en archivos es cómoda, pero no cubre todos los escenarios.
Next.js expone un campo icons dentro del objeto de metadatos que te permite declarar iconos de forma programática. Este enfoque te da control granular sobre los atributos rel, sizes, type e incluso media, todo desde layout.tsx o un page.tsx específico. Repasaremos los pasos prácticos para migrar de archivos estáticos a una configuración basada en metadatos.
Requisitos previos
- Un proyecto Next.js 13+ que use el App Router
- Recursos de iconos (SVG, PNG o ICO) colocados en
public/o en una ruta CDN accesible - Familiaridad con el tipo
Metadatadenext
Paso 1: Definir una configuración de iconos reutilizable
Crea un pequeño helper para reutilizar las mismas definiciones de iconos en distintos layouts. Esto mantiene limpias tus exportaciones de metadatos y hace que las actualizaciones futuras sean sencillas.
// lib/icons.ts
import { Metadata } from 'next';
export const iconConfig = {
icon: [
{ url: '/icons/icon-16x16.png', sizes: '16x16', type: 'image/png' },
{ url: '/icons/icon-32x32.png', sizes: '32x32', type: 'image/png' },
{ url: '/icons/icon.svg', sizes: 'any', type: 'image/svg+xml' },
],
apple: [
{ url: '/icons/apple-touch-icon.png', sizes: '180x180', type: 'image/png' },
],
shortcut: '/icons/favicon.ico',
other: [
{
rel: 'mask-icon',
url: '/icons/safari-pinned-tab.svg',
color: '#0066cc',
},
],
} satisfies Metadata['icons'];
Paso 2: Aplicar la configuración en tu layout raíz
Importa el helper y extiéndelo en el campo icons de tu exportación de metadatos. Esto elimina la necesidad de tener archivos favicon.ico, icon.png y apple-icon.png directamente en app/.
// app/layout.tsx
import type { Metadata } from 'next';
import { iconConfig } from '@/lib/icons';
export const metadata: Metadata = {
title: 'My App',
icons: iconConfig,
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang='en'>
<body>{children}</body>
</html>
);
}
Paso 3: Sobrescribir iconos para una ruta específica
Debido a que los metadatos se fusionan hacia abajo en el árbol de rutas, una página puede extender o reemplazar el conjunto de iconos raíz. Esto es útil para marcas específicas de sección —piensa en un icono distinto para un subdirectorio de blog o un dashboard de inquilino.
// app/dashboard/layout.tsx
import type { Metadata } from 'next';
import { iconConfig } from '@/lib/icons';
export const metadata: Metadata = {
icons: {
...iconConfig,
icon: [
{ url: '/icons/dashboard-32x32.png', sizes: '32x32', type: 'image/png' },
{ url: '/icons/dashboard.svg', sizes: 'any', type: 'image/svg+xml' },
],
},
};
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return <section>{children}</section>;
}
Paso 4: Generar iconos dinámicamente a partir de datos
¿Necesitas un icono que cambie según un feature flag, el rol del usuario o una variante de A/B? Obtén los datos en un componente de servidor y construye el objeto icons al vuelo. El ejemplo siguiente elige una variante de alto contraste cuando la cookie theme indica modo oscuro.
// app/settings/page.tsx
import { cookies } from 'next/headers';
import type { Metadata } from 'next';
export async function generateMetadata(): Promise<Metadata> {
const cookieStore = await cookies();
const theme = cookieStore.get('theme')?.value ?? 'light';
const suffix = theme === 'dark' ? '-dark' : '';
return {
icons: {
icon: [
{ url: `/icons/settings${suffix}-32x32.png`, sizes: '32x32' },
{ url: `/icons/settings${suffix}.svg`, sizes: 'any' },
],
apple: [{ url: `/icons/settings${suffix}-apple.png`, sizes: '180x180' }],
},
};
}
export default function SettingsPage() {
return <main>Settings</main>;
}
Paso 5: Añadir SVG en modo oscuro con media queries
Si prefieres un solo SVG que se adapte mediante CSS, decláralo una vez y deja que el atributo media haga el resto. Esto evita recursos duplicados y mantiene el <head> limpio.
// lib/icons.ts (adición)
export const adaptiveIconConfig = {
icon: [
{
url: '/icons/adaptive.svg',
sizes: 'any',
type: 'image/svg+xml',
media: '(prefers-color-scheme: light)',
},
{
url: '/icons/adaptive-dark.svg',
sizes: 'any',
type: 'image/svg+xml',
media: '(prefers-color-scheme: dark)',
},
],
} satisfies Metadata['icons'];
Errores comunes
- Olvidar la restricción
satisfies. Sin ella, TypeScript no detectará valoresurlfaltantes ni valoresrelinválidos. - Mezclar iconos basados en archivos y en metadatos. Next.js los fusiona, pero las entradas
relduplicadas pueden causar comportamiento impredecible en el navegador. Elige una estrategia por segmento de ruta. - Usar URLs relativas sin la barra inicial. Empieza siempre con
/o un origen CDN absoluto; de lo contrario, el navegador las resolverá contra la ruta actual. - Omitir un ICO de respaldo. Algunos proxies empresariales y rastreadores antiguos todavía solicitan
/favicon.ico. Manténshortcut: '/icons/favicon.ico'en tu configuración.
Lista de verificación
- Abre DevTools → Network → filtra por “img” y confirma que cada icono declarado devuelve 200.
- Inspecciona
<head>en busca de etiquetas<link rel='icon'>,<link rel='apple-touch-icon'>y<link rel='mask-icon'>. - Prueba el cambio a modo oscuro en la configuración del sistema operativo; el SVG correcto debería cargarse sin refrescar.
- Ejecuta una auditoría rápida con el comprobador en línea de Mzu favicondl para detectar tamaños faltantes o discrepancias en el tipo MIME.
Cuándo mantener la convención basada en archivos
Si tu proyecto solo necesita un conjunto de favicon estático y único, el enfoque basado en archivos (app/favicon.ico, app/icon.png, etc.) sigue siendo el más sencillo. No requiere TypeScript, no tiene costo en tiempo de ejecución y funciona de inmediato. Recurre a la API de metadatos cuando necesites sobrescrituras por ruta, selección dinámica o atributos avanzados como media y color.
Para profundizar en el flujo basado en archivos, consulta nuestra guía sobre cómo añadir un favicon en el Next.js App Router en minutos. Y si estás construyendo una PWA, la guía de favicon en el Web App Manifest cubre los iconos del manifiesto que necesitarás junto a estas declaraciones de metadatos.
Consulta los requisitos documentados
Next.js documenta los favicons y los iconos de aplicación en sus convenciones de archivos de metadatos.