Has configurado tus rutas de SvelteKit, pulido los layouts y desplegado la build. Pero cuando abres tu pestaña en vivo, ves el logo de Svelte por defecto — o peor, un cuadrado blanco vacío. La solución suele estar en un archivo específico: app.html.

app.html es la plantilla HTML que SvelteKit usa para renderizar cada página tanto en el servidor como en el cliente. Según la documentación de estructura de proyecto de SvelteKit, esta plantilla se encuentra en la raíz de src/ y es el único lugar donde puedes inyectar marcado que sobrevive a la hidratación en cada ruta. Eso lo convierte en el hogar perfecto para tus etiquetas <link> de favicon.

¿Por qué app.html y no el layout?

Puedes añadir etiquetas de favicon dentro del bloque <svelte:head> de un archivo +layout.svelte, y funcionará para la mayoría de rutas. Pero app.html se renderiza primero, antes de que se ejecute cualquier layout o código de componente. Eso le da dos ventajas reales:

Requisitos previos

Necesitarás:

Si aún no tienes esos archivos, genéralos con Mzu favicondl desde cualquier imagen de origen; genera el paquete completo de tamaños en un clic.

Paso a paso: Añadir etiquetas de favicon a app.html

1. Localiza tu archivo app.html

Abre src/app.html. Por defecto, se ve más o menos así:

<!doctype html>
<html lang='en'>
  <head>
    <meta charset='utf-8' />
    <link rel='icon' href='%sveltekit.assets%/favicon.png' />
    <meta name='viewport' content='width=device-width, initial-scale=1' />
    %sveltekit.head%
  </head>
  <body data-sveltekit-preload-data='hover'>
    <div style='display: contents'>%sveltekit.body%</div>
  </body>
</html>

Observa los placeholders %sveltekit.assets% y %sveltekit.head%; se reemplazan en tiempo de build. Puedes mantenerlos y añadir más etiquetas justo encima o debajo.

2. Reemplaza la línea de favicon por defecto

La plantilla inicial incluye una única línea con favicon.png. Bórrala y reemplázala con un conjunto completo de etiquetas. La especificación HTML define rel='icon' como la forma estándar de declarar iconos, así que usa ese atributo.

<link rel='icon' href='%sveltekit.assets%/favicon.svg' type='image/svg+xml' />
<link rel='icon' href='%sveltekit.assets%/favicon.ico' sizes='32x32' />
<link rel='apple-touch-icon' href='%sveltekit.assets%/apple-touch-icon.png' sizes='180x180' />
<link rel='manifest' href='%sveltekit.assets%/site.webmanifest' />

El SVG va primero para que los navegadores modernos lo prefieran. El ICO actúa como respaldo para navegadores antiguos. Apple Touch y manifest van al final porque se dirigen a plataformas específicas.

3. Añade soporte para modo oscuro

Si tu icono SVG necesita un aspecto diferente en pestañas oscuras (y hoy en día, ¿quién no usa un tema oscuro?), declara dos fuentes con media queries. Esta es la misma técnica que cubrimos en nuestra guía de favicon en modo oscuro:

<link rel='icon' href='%sveltekit.assets%/favicon-light.svg' type='image/svg+xml' media='(prefers-color-scheme: light)' />
<link rel='icon' href='%sveltekit.assets%/favicon-dark.svg' type='image/svg+xml' media='(prefers-color-scheme: dark)' />

El navegador evalúa el atributo media y selecciona el icono correspondiente.

4. Coloca tus archivos en static/

SvelteKit sirve todo lo que está dentro de static/ desde la raíz del sitio. Coloca cada archivo de favicon allí con el nombre exacto que referencias en tus etiquetas. Una estructura limpia se ve así:

static/
├── favicon.svg
├── favicon.ico
├── favicon-light.svg
├── favicon-dark.svg
├── apple-touch-icon.png
└── site.webmanifest

5. Reinicia el servidor de desarrollo

Vite cachea agresivamente la plantilla HTML. Después de editar app.html, detén tu servidor de desarrollo con Ctrl+C y ejecuta npm run dev de nuevo. De lo contrario, podrías seguir viendo marcado obsoleto.

Errores comunes

Poner etiquetas de favicon en el body. Los navegadores solo buscan declaraciones de iconos en <head>. Cualquier cosa dentro de <body> se ignora, incluso si el archivo se llama app.html. Si los tuyos no aparecen, esto es lo primero que debes revisar.

Nombres de archivo que no coinciden entre las etiquetas y static/. La sensibilidad a mayúsculas importa en despliegues Linux. favicon.ICO y favicon.ico son archivos diferentes. Haz coincidir los nombres exactamente.

Iconos obsoletos después del despliegue. Los navegadores cachean los favicons agresivamente — a veces durante semanas. Si cambias archivos y no ves actualizaciones, añade una cadena de consulta o un hash de contenido, y vuelve a desplegar. El mismo patrón funciona para cualquier activo estático en static/.

Atributos SVG con doble codificación. Si copias un fragmento que usa type="image/svg+xml" con comillas dobles dentro de la plantilla app.html, Vite puede dañarlo. Usa comillas simples dentro de los atributos HTML para mantener la build feliz.

Conclusión

La plantilla app.html es el lugar más limpio para declarar favicons en SvelteKit porque se ejecuta antes de cualquier JavaScript y se envía con cada respuesta de página. Coloca una pila completa de SVG + ICO + Apple Touch en static/, conecta las etiquetas <link> correspondientes en <head>, y tendrás un icono nítido en cada pestaña del navegador desde el primer pintado.

Para una visión más amplia de las opciones de favicon en SvelteKit — incluyendo el enfoque con +layout.svelte e iconos dinámicos — consulta nuestra guía principal de favicon en SvelteKit. Y si necesitas los archivos de icono reales, Mzu favicondl genera la pila completa de tamaños desde cualquier imagen de origen en segundos.