If you've ever deployed a Vite app only to see a generic gray square in the browser tab, you know the frustration. It's not a bug; it's a configuration gap. Unlike older frameworks where you might have manually edited an HTML file, Vite relies on a strict file-based convention that often trips up developers migrating from Create React App.

When Stripe updated their branding, they didn't just swap one image file. They ensured every resolution, from the tiny 16x16 tab icon to the massive 512x512 PWA icon, was perfectly optimized. If you want your Vite app to look as professional as Stripe's dashboard, you need to stop guessing and start following the modern standard.

Why Vite Handles Icons Differently

Vite treats your public folder as a static asset root. This means files placed there are served exactly as they are, without processing. However, the magic happens when you name your files correctly. Vite automatically detects specific filenames like favicon.ico or favicon.svg and injects the necessary HTML tags during the build.

This approach eliminates the need for complex plugins or manual <link> tags in your index.html for the primary icon. It's a clean, opinionated workflow that saves time but requires precision. If you misspell the filename, the browser won't find the icon, and you'll be left with that dreaded default gray square.

Step 1: Prepare Your Source Files

Before touching your code, you need the right assets. A single PNG file is rarely enough for 2026 standards. You need an SVG for scalability and a multi-resolution ICO for legacy browser support. If you only have a logo, use Mzu favicondl to generate the necessary sizes instantly.

Ensure your SVG is optimized. Remove hidden layers and unnecessary metadata. A bloated SVG will slow down your initial paint, even if it's just a 16x16 pixel icon. For the ICO file, include 16x16, 32x32, and 48x48 sizes in a single container.

Step 2: Place Files in the Public Directory

Navigate to your project root. You will see a folder named public. This is the only place where static assets live. Drag and drop your favicon.svg and favicon.ico directly into this folder.

Do not put these files in src/assets. If you put them there, Vite will hash the filenames during the build process (e.g., favicon.a1b2c3.svg), which breaks the automatic detection mechanism. The public folder is your safe zone for files that must remain named exactly as they are.

Step 3: Verify the Configuration

Run your development server with npm run dev. Open your browser and inspect the page source. You should see a line similar to this:

<link rel="icon" type="image/svg+xml" href="/favicon.svg" />

If you see this, Vite has successfully detected your icon. If you don't, check your file extension. Vite is case-sensitive. Favicon.svg will not work; it must be favicon.svg. This is a common pitfall that wastes hours of debugging time.

Handling Dark Mode and Apple Touch Icons

A professional setup goes beyond the default tab icon. Modern users expect your site to adapt to their system preferences. If your brand uses a dark background, a white logo might vanish in dark mode. You need a specific SVG variant for this.

Implementing Dark Mode Support

Create a second SVG file named favicon-dark.svg. In your index.html (inside the public folder, not src), add a conditional link tag. This tells the browser to swap the icon based on the prefers-color-scheme media query.

<link rel="icon" type="image/svg+xml" href="/favicon.svg" media="(prefers-color-scheme: light)" /> <link rel="icon" type="image/svg+xml" href="/favicon-dark.svg" media="(prefers-color-scheme: dark)" />

This simple addition ensures your brand remains visible and polished regardless of the user's OS settings. It's a small detail that signals high-quality engineering.

Setting Up the Apple Touch Icon

Mobile users on iOS need a different icon. When they "Add to Home Screen," the browser expects a 180x180 PNG. Place a file named apple-touch-icon.png in your public folder. Vite will automatically serve this file when the browser requests it.

Do not forget the manifest file. Create a manifest.json in the public folder and link it in your index.html. This file tells Android and iOS how to display your app as a standalone PWA. For more details on the manifest structure, check our guide on Web App Manifest Favicon.

Quick Vite Favicon Setup (Copy-Paste)

Vite serves everything in public/ from the site root during development and copies it unchanged into dist/ when you build. That means favicon files go in public/ and are referenced with root-absolute paths in index.html:

public/
  favicon.ico
  favicon.svg
  apple-touch-icon.png
index.html
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">

If the app is deployed under a sub-path, set base in your Vite config, run a build and open dist/index.html to confirm the icon paths point where you expect.

Common Pitfalls and Fixes

Even with the correct setup, things can go wrong. The most frequent issue is caching. Browsers are aggressive about caching favicons. If you update your icon but still see the old one, you aren't seeing a bug; you're seeing a cached version.

To force an update, you can add a query string to your link tag in index.html, like href="/favicon.svg?v=2". This tricks the browser into fetching the new file. Alternatively, clear your browser cache or use an incognito window to verify the change.

Another issue is the 404 error in the console. If your browser console shows a 404 for /favicon.ico, it means the file is missing from the public folder. Even if you use an SVG, some browsers still request the legacy ICO file. Always provide a fallback ICO file to silence these errors.

Finally, remember that Vite's build output goes to the dist folder. When you deploy, ensure your server serves the files from the public directory correctly. If you're using a static host like Vercel or Netlify, this happens automatically. If you're on a custom server, check your routing configuration.

Setting up a favicon for a Vite app doesn't need to be complicated. By respecting the file-based conventions and providing the right formats, you ensure your app looks professional across every device. It's the difference between a hobby project and a production-ready application.

Framework-Specific Vite Favicon Guides

This page covers the general Vite setup. If your problem is specific to one framework or symptom, these narrower guides go deeper: