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 /src get processed by Vite and given hashed filenames. Favicons need stable root paths.
  • useHead for site-wide favicons in Nuxt. It works but runs in every component that imports the composable. Put stable favicons in nuxt.config.ts and reserve useHead for 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.