---
title: Vue Favicon Generator
description: Vue components never render the document head, so favicon links belong in index.html. The path differs between Vite scaffolds and Vue CLI projects.
canonical: https://favicontools.com/platforms/vue-favicon
---

# 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.html  # Vite: the link tags go here
  public/
    favicon.ico  <- add
    favicon-32x32.png  <- add
    apple-touch-icon.png  <- add  # 180x180
    icon-192.png  <- add
    icon-512.png  <- add
    site.webmanifest  <- add
  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

```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) (recommended)
The 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 CLI needs the BASE_URL placeholder:** In public/index.html, write href="<%= BASE_URL %>favicon.ico" — note there is no slash after the placeholder, since publicPath already ends with one. This is the single most common reason a Vue CLI favicon works locally and 404s on GitHub Pages.

## 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.
