Dejaste tu favicon.svg en la carpeta public de Vite, pulsaste guardar y la pestaña del navegador sigue mostrando un cuadrado blanco en blanco. El build funciona, el archivo está ahí mismo y, aun así, el navegador no lo encuentra. Esto ocurre cuando se trata la carpeta public de Vite como cualquier otra carpeta estática y se pasa por alto una regla clave sobre cómo Vite sirve esos archivos.

La carpeta public es la vía de escape de Vite para los assets que necesitan URLs estables y sin hash. Los favicons son el caso de libro: la razón más habitual de confusión entre quienes empiezan con Vite.

Por qué los favicons van en la carpeta public de Vite

Vite aplica un hash a los assets importados desde src/ para invalidar caché (piensa en assets/logo-a3f9c2.svg). Genial para JS y CSS, pero pésimo para favicons. Los navegadores cachean los favicons de forma agresiva y los buscadores necesitan una URL predecible para mostrar tu icono en los resultados.

Los archivos en public/ se saltan el bundler por completo. Según la documentación oficial de Vite, todo lo que esté en este directorio se copia a la raíz del build y se sirve desde la raíz. Así, public/favicon.svg termina siendo /favicon.svg, no /public/favicon.svg. Solo el nombre del archivo, precedido por una barra.

Requisitos previos

Paso 1: coloca los archivos de favicon en public/

Coloca tus archivos de icono directamente en el directorio public/ en la raíz del proyecto. La mayoría de los scaffolds de Vite crean esta carpeta automáticamente.

my-vite-app/
├── public/
│   ├── favicon.svg
│   ├── favicon.ico
│   └── apple-touch-icon.png
├── src/
├── index.html
└── package.json

Usa nombres de archivo simples y convencionales. Los navegadores encuentran favicon.svg, favicon.ico y apple-touch-icon.png en la raíz sin configuración extra.

Paso 2: referencia los archivos con rutas relativas a la raíz

Abre index.html en la raíz del proyecto (no public/index.html: ese archivo no existe en Vite). Añade etiquetas link dentro del <head> usando rutas absolutas que empiecen por /. Es el único punto del proyecto Vite donde se hardcodean rutas de assets, porque la carpeta public se salta a propósito el resolver de imports.

<head>
  <link rel='icon' type='image/svg+xml' href='/favicon.svg'>
  <link rel='icon' type='image/x-icon' href='/favicon.ico'>
  <link rel='apple-touch-icon' href='/apple-touch-icon.png'>
</head>

El estándar HTML define rel='icon' como la forma estándar de declarar favicons (WHATWG HTML Living Standard). Los navegadores eligen la mejor opción según los atributos type y sizes.

Paso 3: añade una variante para modo oscuro

Los navegadores modernos admiten un segundo SVG que se activa automáticamente en temas oscuros. Añade otra etiqueta link con una media query para que los usuarios vean el icono correcto cuando su sistema cambie a modo oscuro.

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

Si quieres profundizar en los favicons SVG para modo oscuro, consulta nuestra guía de favicon en modo oscuro.

Paso 4: comprueba el resultado del build

Ejecuta npm run build e inspecciona dist/. Tus archivos de favicon deberían aparecer en la raíz, sin que ningún bundler los haya tocado.

dist/
├── assets/
├── favicon.svg
├── favicon.ico
└── index.html

También puedes arrancar npm run preview y abrir la pestaña Network en DevTools. Deberías ver peticiones a favicon.svg, favicon.ico y apple-touch-icon.png devolviendo 200, no 404.

Errores habituales

La mayoría de problemas con favicons en Vite vienen de uno de estos errores. Si la pestaña aparece en blanco o muestra un icono genérico, repasa antes esta lista.

Cuando la carpeta public no basta

La carpeta public es el lugar adecuado para favicons estáticos que no cambian. Te ofrece URLs estables que los navegadores cachean de forma fiable y los buscadores pueden indexar. Si más adelante necesitas favicons dinámicos (badges de notificación, avatares de usuario, indicadores de estado en vivo), necesitarás JavaScript para cambiar el atributo href en tiempo de ejecución. Nuestra guía sobre favicon dinámico con JavaScript cubre ese patrón en detalle.

¿Necesitas ayuda para generar antes los archivos de icono correctos? Mzu favicondl produce variantes SVG, PNG, ICO y Apple Touch a partir de una única imagen de origen. Suéltalos en public/, añade tus etiquetas link y tu app Vite tendrá una presencia profesional en la pestaña en cuestión de minutos.