You shipped a new React + Vite app, dropped your favicon.ico into public/, restarted the dev server — and the browser tab still shows the Vite lightning bolt. Or worse, nothing at all. This is the classic React Vite favicon not showing problem, and it almost always traces back to one of a handful of specific misconfigurations.

If you are still setting up icons rather than troubleshooting, our Vite favicon guide covers the full setup.

Here's the two-minute fix first, then the root causes explained.

The 2-Minute Fix

Open index.html in your project root and verify the link tags use absolute paths starting with /, not /public/. In Vite, files in the public/ directory are copied to the build output and served at the root path (see the Vite public directory docs). So favicon.ico in public/favicon.ico is referenced as /favicon.ico.

<link rel='icon' type='image/x-icon' href='/favicon.ico' />
<link rel='icon' type='image/svg+xml' href='/icon.svg' />
<link rel='apple-touch-icon' href='/apple-touch-icon.png' />

Save the file. If Vite is running, HMR picks it up automatically. If the icon still doesn't appear, hard refresh (Ctrl+Shift+R or Cmd+Shift+R) and check the Network tab for a 200 response on the favicon URL.

Root Causes Explained

1. The file is in src/, not public/

This is the number one mistake. Files in src/ go through Vite's bundler — they need to be imported as modules and end up with hashed filenames. Files in public/ are served as-is at the root path. If you put your favicon in src/assets/ and expect it to appear at /favicon.ico, it won't exist there. Move it to public/.

2. Wrong path in the link tag

Developers coming from Create React App sometimes write href='%PUBLIC_URL%/favicon.ico' or href='/public/favicon.ico'. Vite has no %PUBLIC_URL% variable, and /public/ is a literal directory name that doesn't exist at runtime. Use plain /favicon.ico.

3. Base path mismatch

If you set base: '/my-app/' in vite.config.js, every asset path must include that prefix. A link tag with href='/favicon.ico' will 404 when the app is served from https://example.com/my-app/. Fix it with a relative reference (href='./favicon.ico') or update all paths to include the configured base.

4. Cache is lying to you

Browsers aggressively cache favicons. Chrome stores them in a separate SQLite database that survives normal cache clears (we covered this in our favicon cache clear guide). Even after you fix the path, the old broken icon can stick around for hours. A query-string version bump forces a fresh request during development:

<link rel='icon' href='/favicon.ico?v=2' />

5. SPA routing intercepts /favicon.ico

If you're using React Router with a catch-all route, a request for /favicon.ico might be handled by your React app and return HTML instead of the icon. Vite's dev server prioritizes public/ files, so this is mostly a production-build issue. Verify by curling the deployed URL and checking the Content-Type header.

6. Link tags inside React components

<link> tags placed inside JSX components don't always render in <head> the way you'd expect. React doesn't hoist them — they only attach when the component mounts in the DOM. Use react-helmet-async for dynamic icons, or place static favicon tags directly in index.html.

7. SVG favicon syntax errors

Chrome parses SVG favicons strictly per the HTML spec for rel='icon'. Missing namespace declarations, invalid viewBox attributes, or external resource references will silently fail. Test the SVG by opening it directly in a browser tab before referencing it.

Prevention Checklist

For a deeper walkthrough of Vite's file-based metadata approach, see our complete Vite public folder favicon guide. And if your issue is React-specific (Helmet, dynamic icons), the React favicon setup guide covers those patterns.

Final Thoughts

The React Vite favicon not showing problem almost always comes down to file location, path reference, or caching. Get those three right and you're 90% there. The remaining 10% is usually a base-path mismatch or a React component swallowing the link tag — both of which leave clear fingerprints in DevTools.

Generate the full icon set with Mzu favicondl, drop the files into public/, paste the link tags into index.html, and ship it.