Skip to main content
Back to BlogTutorial

How to Add a Favicon in React (CRA & Vite, 2026)

Add a favicon to a React app with Create React App or Vite — file locations, index.html edits, SVG preference, and runtime favicon swapping.

August 20267 min read

Adding a favicon to a React app depends on your build tool: Create React App (CRA) and Vite handle files differently. This guide covers both, plus how to swap favicons at runtime (useful for dark-mode or branded white-label apps).

Create React App (CRA)

CRA puts static files in the public/ folder.

  1. Place favicon.ico in public/.
  2. Open public/index.html and check the <link> tag (CRA includes one by default):
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/apple-touch-icon.png" />
<link rel="manifest" href="%PUBLIC_URL%/site.webmanifest" />

The %PUBLIC_URL% variable ensures the path works regardless of where the app is deployed (subdirectory or root).

Vite + React

Vite is the modern default for new React apps. Favicons go in public/, but the HTML template is index.html at the project root (not inside public/).

  1. Place your favicon files in public/.
  2. Edit the root index.html and add link tags in <head>:
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />

Vite prefers SVG favicons — note the type="image/svg+xml" declaration. Put your favicon.svg in public/ and reference it with a leading slash.

Get every file you need (ICO, PNG sizes, SVG, Apple touch icon, manifest) from one upload at Favicon.one.

Swapping the favicon at runtime

For dark-mode favicons or white-label apps, you can change the favicon dynamically with a small hook:

import { useEffect } from 'react'

function useFavicon(href: string) {
  useEffect(() => {
    let link = document.querySelector("link[rel~='icon']") as HTMLLinkElement
    if (!link) {
      link = document.createElement('link')
      link.rel = 'icon'
      document.head.appendChild(link)
    }
    link.href = href
  }, [href])
}

// Usage
function App() {
  useFavicon('/favicon-dark.svg')
  return <div>...</div>
}

For a full dark-mode setup (automatic switching), see our Favicon Dark Mode guide.

Using react-helmet

If you already use react-helmet or react-helmet-async for managing <head>, you can declare favicons declaratively:

<Helmet>
  <link rel="icon" href="/favicon.ico" />
  <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
</Helmet>

Next.js vs React (CRA/Vite)

If you're using Next.js, the approach is different — Next.js has file-based conventions that auto-generate the tags. See our dedicated Next.js favicon guide.

Common React favicon problems

  • Hard refresh. Vite's dev server caches aggressively. Stop and restart npm run dev if your icon doesn't update.
  • Wrong folder. CRA and Vite both want files in public/, not src/.
  • Missing leading slash. Use /favicon.ico, not favicon.ico, so the path resolves from root.
  • Build output missing the file. Check dist/ (Vite) or build/ (CRA) after building to confirm the favicon was copied.

Still stuck? See the full “Favicon Not Showing” checklist.

Ready to generate your favicon?

Upload your image and get a complete favicon package in seconds. Free, private, and processed right in your browser.