Vue.js Favicon Complete Guide

Master favicon implementation in Vue.js: Vue 3, Vite, Nuxt.js, dynamic icons, composition API, and PWA integration.

Vue Favicon Quick Facts

  • Location: /public folder
  • Build Tool: Vite (recommended)
  • Framework: Vue 3 + Composition API
  • SSR: Nuxt.js support
  • Dynamic: Composables & hooks
  • PWA: vite-plugin-pwa

Vue 3 + Vite Setup (Recommended)

Modern Vue 3 Setup

1. File Structure

my-vue-app/
  public/               ? Place all favicons here
    favicon.ico
    favicon-16x16.png
    favicon-32x32.png
    apple-touch-icon.png
    android-chrome-192x192.png
    android-chrome-512x512.png
    site.webmanifest
  src/
    main.js
    App.vue
  index.html          ? Root level
  vite.config.js

2. Update index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <!-- Favicons -->
    <link rel="icon" type="image/x-icon" href="/favicon.ico">
    <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
    <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
    <link rel="apple-touch-icon" href="/apple-touch-icon.png">
    <link rel="manifest" href="/site.webmanifest">
    <meta name="theme-color" content="#42b883">
    
    <title>Vue App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>
Vite Auto-Serving: Files in /public are served at root URL. Changes require page refresh.

Dynamic Favicon with Composition API

Vue 3 Composables

Create useFavicon Composable

// composables/useFavicon.js
import { watch, onMounted } from 'vue'

export function useFavicon(href) {
  const updateFavicon = (newHref) => {
    let link = document.querySelector("link[rel~='icon']")
    
    if (!link) {
      link = document.createElement('link')
      link.rel = 'icon'
      document.head.appendChild(link)
    }
    
    link.href = newHref
  }

  onMounted(() => {
    if (href.value) {
      updateFavicon(href.value)
    }
  })

  watch(href, (newHref) => {
    updateFavicon(newHref)
  })

  return { updateFavicon }
}

// Usage in component:
import { ref } from 'vue'
import { useFavicon } from './composables/useFavicon'

export default {
  setup() {
    const faviconPath = ref('/favicon.ico')
    useFavicon(faviconPath)
    
    const showNotification = () => {
      faviconPath.value = '/favicon-notification.ico'
    }
    
    return { showNotification }
  }
}

Notification Badge Example

// composables/useFaviconBadge.js
import { ref, watch } from 'vue'

export function useFaviconBadge(count) {
  const canvas = document.createElement('canvas')
  canvas.width = 32
  canvas.height = 32
  const ctx = canvas.getContext('2d')

  const drawBadge = (number) => {
    const favicon = document.querySelector("link[rel~='icon']")
    const img = new Image()
    img.src = favicon?.href || '/favicon.ico'
    
    img.onload = () => {
      ctx.clearRect(0, 0, 32, 32)
      ctx.drawImage(img, 0, 0, 32, 32)
      
      if (number > 0) {
        // Badge background
        ctx.fillStyle = '#ff0000'
        ctx.beginPath()
        ctx.arc(24, 8, 8, 0, 2 * Math.PI)
        ctx.fill()
        
        // Badge text
        ctx.fillStyle = '#ffffff'
        ctx.font = 'bold 10px Arial'
        ctx.textAlign = 'center'
        ctx.fillText(number > 9 ? '9+' : number.toString(), 24, 11)
      }
      
      favicon.href = canvas.toDataURL('image/png')
    }
  }

  watch(count, (newCount) => {
    drawBadge(newCount)
  })

  return { drawBadge }
}

// Usage:
import { ref } from 'vue'
import { useFaviconBadge } from './composables/useFaviconBadge'

export default {
  setup() {
    const unreadCount = ref(0)
    useFaviconBadge(unreadCount)
    
    return { unreadCount }
  }
}

Nuxt.js Integration

Server-Side Rendering Support

For Nuxt.js setup, see our dedicated guide:

Nuxt.js Favicon Guide

Vue.js Favicon Best Practices

? Best Practices

  • Place favicons in /public folder
  • Use absolute paths (/favicon.ico)
  • Create composables for reusable logic
  • Use ref/reactive for dynamic updates
  • Test with Vite dev server
  • Include PWA manifest
  • Leverage Composition API
  • Verify production build

? Common Mistakes

  • Placing favicons in /src
  • Using relative paths
  • Importing favicons in components
  • Not testing production build
  • Missing browser cache clear
  • Forgetting theme-color meta
  • Not using composables properly
  • Ignoring SSR considerations

Generate Vue-Ready Favicons

Create optimized favicon packages for Vue.js applications

Generate Favicons

Related Articles

An unhandled error has occurred. Reload 🗙