Platform Guides

React Favicon Setup: Complete Implementation Guide

How to properly add favicons to your React application using Vite. Covers public directory setup and HTML configuration.

5 min read
Free Guide

React Favicons: Simpler Than You Think

Here's a secret: adding favicons to a React app is refreshingly straightforward. No webpack configs, no complex build processes - just put files in the right place and reference them correctly. Whether you're using Create React App, Vite, or a custom setup, the principles remain the same.

This guide focuses on modern React applications, particularly those using Vite for vanilla React SPAs (the de facto choice for client‑side builds), while Next.js remains the de facto option for full‑stack React apps. If you're still on Create React App, don't worry - the concepts translate directly.

The Public Directory Method

Basic Setup (Vite)

In a Vite-powered React app, your project structure looks like this:

my-react-app/
├── public/
│   └── favicon.ico
├── src/
└── index.html

Simply place your favicon in the public directory and reference it in your index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="icon" type="image/x-icon" href="/favicon.ico">
    <title>My React App</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

The leading slash is crucial - it ensures the favicon works regardless of your routing.

Multiple Favicon Sizes

For comprehensive device support, add multiple files:

public/
├── favicon.ico
├── favicon-16x16.png
├── favicon-32x32.png
├── apple-touch-icon.png
└── android-chrome-192x192.png

Update your index.html:

<head>
  <!-- Standard 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">
  
  <!-- Apple devices -->
  <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
  
  <!-- Android devices -->
  <link rel="icon" type="image/png" sizes="192x192" href="/android-chrome-192x192.png">
</head>

Dynamic Favicons in React

Want to change favicons based on app state? Here's how:

React Hook for Dynamic Favicons

// hooks/useFavicon.ts
import { useEffect } from 'react';

export const useFavicon = (href: string) => {
  useEffect(() => {
    const link: HTMLLinkElement = 
      document.querySelector("link[rel*='icon']") || 
      document.createElement('link');
    
    link.type = 'image/x-icon';
    link.rel = 'shortcut icon';
    link.href = href;
    
    document.getElementsByTagName('head')[0].appendChild(link);
  }, [href]);
};

Usage in your app:

// App.tsx
import { useFavicon } from './hooks/useFavicon';

function App() {
  const hasNotifications = useNotificationCount() > 0;
  
  useFavicon(hasNotifications ? '/favicon-alert.ico' : '/favicon.ico');
  
  return <YourApp />;
}

Environment-Based Favicons

Show different favicons for dev/staging/production:

// App.tsx
const getFaviconByEnv = () => {
  if (import.meta.env.DEV) return '/favicon-dev.ico';
  if (import.meta.env.VITE_APP_ENV === 'staging') return '/favicon-staging.ico';
  return '/favicon.ico';
};

function App() {
  useFavicon(getFaviconByEnv());
  // ...
}

SVG Favicons

SVG favicons offer perfect scaling and tiny file sizes:

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

Create a simple SVG favicon:

<!-- public/favicon.svg -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
  <rect width="32" height="32" rx="4" fill="#000"/>
  <text x="16" y="24" text-anchor="middle" fill="#fff" font-family="Arial" font-size="20">R</text>
</svg>

Dark Mode Support with SVG

SVG favicons can adapt to dark mode:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
  <style>
    rect { fill: #000; }
    text { fill: #fff; }
    @media (prefers-color-scheme: dark) {
      rect { fill: #fff; }
      text { fill: #000; }
    }
  </style>
  <rect width="32" height="32" rx="4"/>
  <text x="16" y="24" text-anchor="middle" font-family="Arial" font-size="20">R</text>
</svg>

Web App Manifest

For PWA capabilities, add a manifest:

// public/manifest.json
{
  "name": "My React App",
  "short_name": "React App",
  "icons": [
    {
      "src": "/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": "/",
  "display": "standalone",
  "theme_color": "#000000",
  "background_color": "#ffffff"
}

Link it in your HTML:

<link rel="manifest" href="/manifest.json">

Create React App Specifics

If you're using Create React App, the setup is nearly identical:

public/
├── favicon.ico          # Automatically linked
├── logo192.png          # PWA icon
├── logo512.png          # PWA icon
└── manifest.json        # PWA manifest

CRA automatically includes these in the build. Just replace the default files with your own.

Handling Cache Issues

Favicons are aggressively cached. Force updates with cache busting:

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

Or programmatically:

const updateFavicon = (href: string) => {
  const link = document.querySelector("link[rel*='icon']") as HTMLLinkElement;
  link.href = `${href}?v=${Date.now()}`;
};

Testing Your Favicons

Development Testing

  1. 1Check the Network tab: Ensure favicon loads without 404s
  2. 2Test different routes: React Router shouldn't break favicon paths
  3. 3Try incognito mode: Bypasses aggressive caching

Production Testing

# Build and preview locally
npm run build
npm run preview

Check that all favicon paths resolve correctly in the production build.

Common Pitfalls and Solutions

Favicon Not Showing?

  • Missing leading slash: Use /favicon.ico, not favicon.ico
  • Wrong public path: Ensure Vite/webpack base URL is configured
  • Cache issues: Hard refresh or clear browser data

Works in Dev, Not in Production?

Check your build tool's public path configuration:

// vite.config.js
export default {
  base: '/your-subdirectory/', // If not serving from root
}

React Router Breaking Favicons?

Ensure you're using absolute paths (/favicon.ico) not relative paths.

Advanced Patterns

Notification Badges

Add notification counts to your favicon:

const addBadgeToFavicon = (count: number) => {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const img = new Image();
  
  img.onload = () => {
    canvas.width = 32;
    canvas.height = 32;
    ctx.drawImage(img, 0, 0);
    
    if (count > 0) {
      // Draw red circle
      ctx.fillStyle = '#ff0000';
      ctx.beginPath();
      ctx.arc(24, 8, 8, 0, 2 * Math.PI);
      ctx.fill();
      
      // Draw count
      ctx.fillStyle = '#ffffff';
      ctx.font = 'bold 10px Arial';
      ctx.textAlign = 'center';
      ctx.fillText(count.toString(), 24, 12);
    }
    
    // Update favicon
    const link = document.querySelector("link[rel*='icon']") as HTMLLinkElement;
    link.href = canvas.toDataURL('image/png');
  };
  
  img.src = '/favicon-32x32.png';
};

Animated Favicons

While not recommended for most apps, it's possible:

const animateFavicon = (frames: string[], interval: number = 500) => {
  let currentFrame = 0;
  
  setInterval(() => {
    const link = document.querySelector("link[rel*='icon']") as HTMLLinkElement;
    link.href = frames[currentFrame];
    currentFrame = (currentFrame + 1) % frames.length;
  }, interval);
};

Creating Favicons for React

Need to generate all these favicon sizes? Try Unwrite's Favicon Generator. Upload your logo once and get all the sizes you need for your React app, from tiny 16x16 favicons to large 512x512 PWA icons. Everything processes in your browser - your logo stays private.

Quick Reference Checklist

  • [ ] Add favicon.ico to public directory
  • [ ] Include multiple sizes for better device support
  • [ ] Add Apple touch icon for iOS
  • [ ] Create manifest.json for PWA support
  • [ ] Use absolute paths in HTML references
  • [ ] Test in production build
  • [ ] Consider SVG for scalability
  • [ ] Implement dynamic favicons if needed

Remember: favicons in React are just static files. Don't overcomplicate it. Put them in public, reference them in HTML, and move on to building features that matter.