Vue / Nuxt
Favicons in Vue and Nuxt — the public folder and head config
Vue with Vite uses /public for favicons and index.html for link tags. Nuxt 3 adds app.head config for declarative setup. This guide covers both.
Vue 3 with Vite and Nuxt 3 take slightly different approaches to favicons. Vue gives you an index.html to edit; Nuxt gives you declarative head config. This guide covers both.
Vue 3 with Vite
public/
├── favicon.svg
├── favicon.ico
├── favicon-96x96.png
├── apple-touch-icon.png
├── web-app-manifest-192x192.png
├── web-app-manifest-512x512.png
└── site.webmanifest
Edit /index.html at the project root:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png" />
<link rel="shortcut icon" href="/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<meta name="theme-color" content="#0a0a0a" />
<title>My App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
Same structure as any Vite-based project. No Vue-specific imports or components.
Nuxt 3
Nuxt has two favicon paths: file-based and config-based. The canonical approach is config-based via nuxt.config.ts:
export default defineNuxtConfig({
app: {
head: {
link: [
{ rel: "icon", type: "image/svg+xml", href: "/favicon.svg" },
{ rel: "icon", type: "image/png", sizes: "96x96", href: "/favicon-96x96.png" },
{ rel: "shortcut icon", href: "/favicon.ico" },
{ rel: "apple-touch-icon", sizes: "180x180", href: "/apple-touch-icon.png" },
{ rel: "manifest", href: "/site.webmanifest" },
],
meta: [
{ name: "theme-color", content: "#0a0a0a" },
],
},
},
});
Nuxt merges this into the rendered HTML server-side. The browser sees favicon tags in the initial response.
Favicon files live in /public at the project root, same as Vue-Vite. Nuxt copies /public to the build output unchanged.
Per-page overrides in Nuxt
To override a favicon on a specific route, use useHead inside a page component:
<script setup>
useHead({
link: [
{ rel: "icon", type: "image/svg+xml", href: "/favicon-alt.svg" },
],
});
</script>
useHead appends to the global head config, so you keep base favicons and override only the SVG. Use sparingly; in most cases the global config is enough.
The manifest
public/site.webmanifest:
{
"name": "Your Site",
"short_name": "YourSite",
"start_url": "/",
"display": "standalone",
"theme_color": "#0a0a0a",
"background_color": "#0a0a0a",
"icons": [
{ "src": "/web-app-manifest-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" },
{ "src": "/web-app-manifest-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
]
}
Served from /site.webmanifest in both Vue-Vite and Nuxt. Adapters that set MIME types (Vercel, Netlify, Cloudflare) handle .webmanifest as application/manifest+json automatically.
Common pitfalls
/src/assets/instead of/public. Assets inside/srcget processed by Vite and given hashed filenames. Favicons need stable root paths.useHeadfor site-wide favicons in Nuxt. It works but runs in every component that imports the composable. Put stable favicons innuxt.config.tsand reserveuseHeadfor per-page overrides.- Forgetting
type="image/svg+xml". Some browsers refuse to load the SVG favicon without it. - Pinia or other state-management imports. Do not import favicon URLs through state. Hardcode the public paths.
Generate the set
Drop your logo SVG into Faviconry, unzip into /public. For Vue-Vite paste the five <link> tags into index.html. For Nuxt paste them into app.head.link in nuxt.config.ts.
Frequently asked questions
Do I edit index.html or nuxt.config.ts for Nuxt favicons?
Both work, but nuxt.config.ts is the canonical location. Put link tags in app.head.link inside defineNuxtConfig. Nuxt injects them server-side, which means the favicon appears in the initial HTML response, not after hydration.
Where do favicon files go in a Vue 3 Vite project?
Inside /public at the project root. Files there are served at the root in dev and copied to the build output. Reference them from index.html with absolute paths like /favicon.svg.
Does Nuxt auto-add a favicon?
Nuxt 3 ships a placeholder <link rel="icon" type="image/x-icon" href="/favicon.ico" /> by default if /public/favicon.ico exists. To ship the full modern set you override app.head.link in nuxt.config.ts with all five links.
Other frameworks
- React React itself has no favicon convention. Your bundler does. This guide covers the modern setup for Vite-powered React, Create React App, and framework-less setups.
- SvelteKit SvelteKit's favicon setup lives in two places: /static for the files and src/app.html for the link tags. Here is the current recommended configuration.