Gatsby Favicon Complete Guide

Master favicon setup in Gatsby: static folder configuration, gatsby-plugin-manifest, React Helmet integration, GraphQL queries, and best practices for React-based static sites.

Gatsby Favicon Setup

1. Static Folder

Place in static/

2. Plugin Setup

Configure manifest plugin

3. React Helmet

Add to layout component

Method 1: Static Folder (Recommended)

Simplest Approach

File Structure

my-gatsby-site/
  static/
    favicon.ico           ? Place here
    favicon-16x16.png
    favicon-32x32.png
    favicon-96x96.png
    favicon-512x512.png
    apple-touch-icon.png
    android-chrome-192x192.png
    android-chrome-512x512.png
    site.webmanifest
  src/
    components/
      layout.js
      seo.js
    pages/
      index.js
  gatsby-config.js
Note: Files in static/ are copied to public/ during build and accessible at root URL (e.g., /favicon.ico).

SEO Component with React Helmet

File: src/components/seo.js

import React from 'react'
import { Helmet } from 'react-helmet'
import { useStaticQuery, graphql } from 'gatsby'

function SEO({ title, description }) {
  const { site } = useStaticQuery(
    graphql`
      query {
        site {
          siteMetadata {
            title
            description
          }
        }
      }
    `
  )

  const metaDescription = description || site.siteMetadata.description

  return (
    <Helmet>
      <title>{title} | {site.siteMetadata.title}</title>
      <meta name="description" content={metaDescription} />
      
      {/* 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" sizes="180x180" href="/apple-touch-icon.png" />
      <link rel="manifest" href="/site.webmanifest" />
      <meta name="theme-color" content="#663399" />
    </Helmet>
  )
}

export default SEO

Use in Layout

File: src/components/layout.js

import React from 'react'
import SEO from './seo'

const Layout = ({ children, pageTitle, pageDescription }) => {
  return (
    <>
      <SEO title={pageTitle} description={pageDescription} />
      <main>{children}</main>
    </>
  )
}

export default Layout

Method 2: gatsby-plugin-manifest

PWA-Ready Setup

Install Plugin

npm install gatsby-plugin-manifest

Configure gatsby-config.js

module.exports = {
  siteMetadata: {
    title: 'My Gatsby Site',
    description: 'A Gatsby site with proper favicons',
    siteUrl: 'https://mygatsby.com',
  },
  plugins: [
    'gatsby-plugin-react-helmet',
    {
      resolve: 'gatsby-plugin-manifest',
      options: {
        name: 'My Gatsby Site',
        short_name: 'Gatsby Site',
        start_url: '/',
        background_color: '#ffffff',
        theme_color: '#663399',
        display: 'standalone',
        icon: 'src/images/icon.png', // Source image (512x512 or higher)
        icons: [
          {
            src: '/android-chrome-192x192.png',
            sizes: '192x192',
            type: 'image/png',
          },
          {
            src: '/android-chrome-512x512.png',
            sizes: '512x512',
            type: 'image/png',
          },
        ],
        // Optional: Generate different sizes
        icon_options: {
          purpose: 'any maskable',
        },
        // Include favicon in head section
        include_favicon: true,
        // Legacy support
        legacy: true,
        // Cache busting mode
        cache_busting_mode: 'query',
        // Crossorigin attribute
        crossOrigin: 'use-credentials',
      },
    },
  ],
}
Important: gatsby-plugin-manifest automatically generates multiple icon sizes from the source icon image. Provide a high-resolution PNG (512x512 or larger).

What the Plugin Generates

  • ? Multiple favicon sizes (16x16, 32x32, 48x48, etc.)
  • ? Apple Touch Icons (120x120, 152x152, 167x167, 180x180)
  • ? Android Chrome icons (192x192, 512x512)
  • ? manifest.webmanifest file
  • ? Automatic <link> tags in <head>

Advanced Configuration

Custom Favicon Paths

Using GraphQL for Dynamic Favicons

import React from 'react'
import { Helmet } from 'react-helmet'
import { useStaticQuery, graphql } from 'gatsby'

function SEO({ title }) {
  const data = useStaticQuery(graphql`
    query {
      site {
        siteMetadata {
          title
          siteUrl
        }
      }
      favicon32: file(relativePath: { eq: "favicon-32x32.png" }) {
        publicURL
      }
      favicon16: file(relativePath: { eq: "favicon-16x16.png" }) {
        publicURL
      }
      appleTouchIcon: file(relativePath: { eq: "apple-touch-icon.png" }) {
        publicURL
      }
    }
  `)

  return (
    <Helmet>
      <title>{title}</title>
      <link rel="icon" type="image/png" sizes="32x32" href={data.favicon32.publicURL} />
      <link rel="icon" type="image/png" sizes="16x16" href={data.favicon16.publicURL} />
      <link rel="apple-touch-icon" href={data.appleTouchIcon.publicURL} />
    </Helmet>
  )
}

export default SEO

Environment-Specific Favicons

import React from 'react'
import { Helmet } from 'react-helmet'

function SEO() {
  const isDev = process.env.NODE_ENV === 'development'
  const faviconPath = isDev ? '/favicon-dev.ico' : '/favicon.ico'

  return (
    <Helmet>
      <link rel="icon" type="image/x-icon" href={faviconPath} />
    </Helmet>
  )
}

export default SEO

Dark Mode Favicon

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

Build & Deploy

Production Deployment

Build Process

# Development
gatsby develop
# Favicons available at http://localhost:8000/favicon.ico

# Production build
gatsby build
# Files copied from static/ to public/

# Test production build locally
gatsby serve
# Test at http://localhost:9000

Verify After Build

  1. Check public/ folder contains all favicon files
  2. Verify public/manifest.webmanifest exists
  3. Check HTML files have favicon <link> tags
  4. Test in browser: http://localhost:9000/favicon.ico

Deployment Platforms

Platform Favicon Handling Notes
Netlify ? Automatic Deploys public/ folder
Vercel ? Automatic Static files served from root
Gatsby Cloud ? Optimized CDN-served favicons
GitHub Pages ? Supported Update pathPrefix if needed

Gatsby Favicon Best Practices

? Best Practices

  • Use static/ folder for all favicons
  • Install gatsby-plugin-manifest for PWA
  • Use React Helmet for dynamic <head>
  • Provide high-res source image (512x512+)
  • Test build output in public/ folder
  • Use GraphQL queries for dynamic paths
  • Cache bust with gatsby clean
  • Test on multiple browsers after deploy

? Common Mistakes

  • Placing favicons in src/ instead of static/
  • Forgetting to run gatsby build to test
  • Not using gatsby-plugin-manifest for PWA
  • Missing React Helmet plugin
  • Using low-resolution source images
  • Not clearing cache (.cache/ folder)
  • Incorrect paths in production (missing /)
  • Not testing favicon after deployment

Generate Gatsby-Ready Favicons

Create optimized favicon packages for Gatsby static sites

Generate Favicons

Related Articles

An unhandled error has occurred. Reload 🗙