Contentful Favicon Complete Guide

Master favicon setup with Contentful headless CMS: Asset uploads, GraphQL/REST API queries, content models, and frontend integration for React, Next.js, Vue, and more.

Headless CMS Favicon Approach

1. Upload Assets

Store favicons in Contentful Media

2. Content Model

Create settings content type

3. Query & Render

Fetch via API in frontend

Step 1: Upload Favicon Assets

Contentful Media Library

Upload via Web Interface

  1. Log in to Contentful: app.contentful.com
  2. Select your Space
  3. Go to Media tab
  4. Click Add asset ? Upload files
  5. Upload all favicon files:
    • favicon.ico
    • favicon-16x16.png
    • favicon-32x32.png
    • favicon-96x96.png
    • favicon-512x512.png
    • apple-touch-icon.png
    • android-chrome-192x192.png
    • android-chrome-512x512.png
  6. Click Publish for each asset
  7. Note each asset's ID for reference

Upload via CLI (Automated)

# Install Contentful CLI
npm install -g contentful-cli

# Login
contentful login

# Select space
contentful space use

# Upload asset (example script)
contentful space asset create \
  --file-path ./favicon.ico \
  --file-name "favicon.ico" \
  --title "Site Favicon" \
  --description "Main site favicon"

Step 2: Create Content Model

Site Settings Content Type

Create Content Type

  1. Go to Content model tab
  2. Click Add content type
  3. Name: Site Settings
  4. API Identifier: siteSettings
  5. Create

Add Favicon Fields

Field Name Field ID Type Settings
Favicon ICO faviconIco Media (one file) Accept: image/x-icon
Favicon 16x16 favicon16 Media (one file) Accept: image/png
Favicon 32x32 favicon32 Media (one file) Accept: image/png
Apple Touch Icon appleTouchIcon Media (one file) Accept: image/png

Create Settings Entry

  1. Go to Content tab
  2. Click Add entry ? Site Settings
  3. Link uploaded favicon assets to each field
  4. Click Publish

Step 3: Query with GraphQL

Fetch Favicon Data

GraphQL Query

query {
  siteSettingsCollection(limit: 1) {
    items {
      faviconIco {
        url
        title
        description
      }
      favicon16 {
        url
        width
        height
      }
      favicon32 {
        url
        width
        height
      }
      appleTouchIcon {
        url
        width
        height
      }
    }
  }
}

Example Response

{
  "data": {
    "siteSettingsCollection": {
      "items": [
        {
          "faviconIco": {
            "url": "//images.ctfassets.net/space_id/asset_id/favicon.ico",
            "title": "Site Favicon",
            "description": "Main favicon"
          },
          "favicon16": {
            "url": "//images.ctfassets.net/space_id/asset_id/favicon-16x16.png",
            "width": 16,
            "height": 16
          },
          "favicon32": {
            "url": "//images.ctfassets.net/space_id/asset_id/favicon-32x32.png",
            "width": 32,
            "height": 32
          },
          "appleTouchIcon": {
            "url": "//images.ctfassets.net/space_id/asset_id/apple-touch-icon.png",
            "width": 180,
            "height": 180
          }
        }
      ]
    }
  }
}

Frontend Implementation

React / Next.js Example

Next.js with App Router

File: app/layout.tsx

import { Metadata } from 'next'

async function getSiteSettings() {
  const response = await fetch(
    `https://graphql.contentful.com/content/v1/spaces/${process.env.CONTENTFUL_SPACE_ID}`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${process.env.CONTENTFUL_ACCESS_TOKEN}`,
      },
      body: JSON.stringify({
        query: `
          query {
            siteSettingsCollection(limit: 1) {
              items {
                faviconIco { url }
                favicon16 { url }
                favicon32 { url }
                appleTouchIcon { url }
              }
            }
          }
        `,
      }),
    }
  )
  const data = await response.json()
  return data.data.siteSettingsCollection.items[0]
}

export async function generateMetadata(): Promise<Metadata> {
  const settings = await getSiteSettings()

  return {
    icons: {
      icon: [
        { url: `https:${settings.favicon16.url}`, sizes: '16x16', type: 'image/png' },
        { url: `https:${settings.favicon32.url}`, sizes: '32x32', type: 'image/png' },
        { url: `https:${settings.faviconIco.url}`, type: 'image/x-icon' },
      ],
      apple: { url: `https:${settings.appleTouchIcon.url}`, sizes: '180x180' },
    },
  }
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

React with React Helmet

import { Helmet } from 'react-helmet'
import { useEffect, useState } from 'react'

function App() {
  const [favicons, setFavicons] = useState(null)

  useEffect(() => {
    fetchFavicons().then(setFavicons)
  }, [])

  if (!favicons) return null

  return (
    <>
      <Helmet>
        <link rel="icon" type="image/x-icon" href={`https:${favicons.faviconIco.url}`} />
        <link rel="icon" type="image/png" sizes="16x16" href={`https:${favicons.favicon16.url}`} />
        <link rel="icon" type="image/png" sizes="32x32" href={`https:${favicons.favicon32.url}`} />
        <link rel="apple-touch-icon" sizes="180x180" href={`https:${favicons.appleTouchIcon.url}`} />
      </Helmet>
      {/* Your app content */}
    </>
  )
}

REST API Alternative

Content Delivery API

REST API Request

GET https://cdn.contentful.com/spaces/{SPACE_ID}/entries?content_type=siteSettings&include=2
Authorization: Bearer {ACCESS_TOKEN}

JavaScript Example

const contentful = require('contentful')

const client = contentful.createClient({
  space: process.env.CONTENTFUL_SPACE_ID,
  accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
})

async function getFavicons() {
  const response = await client.getEntries({
    content_type: 'siteSettings',
    limit: 1,
  })

  const settings = response.items[0].fields
  
  return {
    faviconIco: `https:${settings.faviconIco.fields.file.url}`,
    favicon16: `https:${settings.favicon16.fields.file.url}`,
    favicon32: `https:${settings.favicon32.fields.file.url}`,
    appleTouchIcon: `https:${settings.appleTouchIcon.fields.file.url}`,
  }
}

Contentful Favicon Best Practices

? Recommendations

  • Use GraphQL for efficient queries
  • Cache API responses in frontend
  • Use Contentful CDN URLs (fast delivery)
  • Organize favicons in dedicated folder
  • Use environment variables for tokens
  • Implement fallback favicons locally
  • Leverage Contentful's image optimization
  • Version assets for cache busting

? Common Mistakes

  • Forgetting to publish assets
  • Not using HTTPS protocol in URLs
  • Hardcoding space IDs in client code
  • Not caching API responses
  • Missing fallback for API failures
  • Not validating asset URLs
  • Querying on every page load (no cache)
  • Exposing API tokens in frontend

Generate Contentful-Ready Favicons

Create favicon packages optimized for headless CMS deployment

Generate Favicons

Related Articles

An unhandled error has occurred. Reload 🗙