Vue Favicon Generator
A Vue single-file component cannot render a link tag into the head. The icons are declared in index.html, which lives at the project root under Vite and inside public/ under Vue CLI.
Where the files go
- my-vue-app/
- index.htmlVite: the link tags go here
- public/
- favicon.icoadd
- favicon-32x32.pngadd
- apple-touch-icon.pngadd180x180
- icon-192.pngadd
- icon-512.pngadd
- site.webmanifestadd
- src/
- App.vue
- main.js
- vite.config.js
A create-vue scaffold. Vue CLI projects have no root index.html — the template is public/index.html, sitting in the same directory as the icons.
Why it isn't a component
The browser requests the favicon while it parses the head, long before Vue has mounted anything. Anything you add from inside the app arrives after that request has already gone out, which is why the icon belongs in the static HTML rather than in a component.
That also means the dev server's hot reload does not apply here. Editing index.html triggers a full page reload, and swapping a file in public/ often needs a hard refresh before the browser stops using the copy it cached.
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" sizes="any" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<title>My Vue App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>Vite scaffolds vs Vue CLI
create-vue (Vite)
Start hereThe current scaffold
- index.html at the project root, treated as the build entry
- public/ copied to the output root with filenames intact
- Root-absolute hrefs: /favicon.ico
- Sub-path deploys are handled by base in vite.config.js
Vue CLI
webpack, maintenance mode
- The template is public/index.html and is run through EJS
- URLs must be written as <%= BASE_URL %>favicon.ico
- publicPath in vue.config.js sets what BASE_URL expands to
- A bare leading slash silently breaks sub-path deploys
Vue favicon FAQ
Can I set the favicon from a Vue component?
You can, with useHead from unhead or by mutating the link element directly, and browsers do pick up an icon swap after the page has loaded. But the first request still uses whatever index.html declared, so keep a real default there and treat the runtime change as an override.
Why did my favicon vanish after I imported it in a component?
Importing an image from src/ hands it to the bundler, which emits it under a hashed filename. Nothing is then reachable at /favicon.ico. Files in public/ skip the bundler entirely and keep their names, which is exactly what a fixed icon path needs.
Do I need a manifest for a plain Vue SPA?
Only if you want the site to be installable. Without one, Android falls back to the largest icon it can find in your link tags, and the install prompt does not appear. The manifest is also where the name and theme colour on the home screen come from.