Sådan tilføjer du favicon til dit Next.js-projekt: Komplet implementeringsguide 2025

Favicon.im

Favicons er afgørende for moderne webapplikationer og vises i browserfaner, bogmærker, mobilstartskærme og PWA-installationer. Next.js tilbyder flere implementeringsmetoder afhængigt af din routerkonfiguration og funktionskrav.

Denne omfattende guide giver alt, hvad du behøver for at implementere professionelle favicon-systemer i Next.js-projekter, fra grundlæggende opsætning til avancerede dynamiske funktioner.

Det lærer du:

  • Next.js 13+ App Router favicon-implementering
  • Ældre Pages Router-kompatibilitetsmetoder
  • Dynamiske favicon-opdateringer og tematilpasning
  • PWA- og multi-enhedsoptimering
  • Ydelsesoptimering og fejlfinding
  • Virkelige kodeeksempler og bedste praksis

Hurtig start: Grundlæggende favicon-opsætning (5 minutter)

Trin 1: Generér dine favicon-filer

Anbefalet værktøj: Brug RealFaviconGenerator eller Favicon.io for professionelle resultater.

Grundlæggende filstruktur:

public/
├── favicon.ico          # Universal kompatibilitet (16x16, 32x32)
├── favicon-16x16.png   # Browserfaner (ældre understøttelse)
├── favicon-32x32.png   # Højtopløselige browserfaner
├── apple-touch-icon.png # 180x180 (iOS-startskærm)
├── android-chrome-192x192.png # Android-startskærm
├── android-chrome-512x512.png # PWA og højtopløselige skærme
└── site.webmanifest    # Progressive Web App-manifest

Trin 2: Hurtig grundlæggende opsætning

Nulkonfigurationsmetode: Placer favicon.ico i public-mappen. Next.js serverer den automatisk på /favicon.ico.

Hurtig verifikation: Besøg http://localhost:3000/favicon.ico for at bekræfte, at filen er tilgængelig.

Next.js 13+ App Router-implementering

Metode 1: Metadata API-konfiguration (anbefalet)

Hvorfor denne metode: Typesikker, indbygget Next.js-understøttelse, automatisk optimering, bedre SEO.

Next.js App Router understøtter filbaseret favicon-konfiguration:

// app/layout.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'My Next.js App',
  description: 'Amazing Next.js application',
  icons: {
    icon: '/favicon.ico',
    shortcut: '/favicon-16x16.png',
    apple: '/apple-touch-icon.png',
  },
}

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

Metode 2: Professionel multi-enheds-konfiguration

Til produktionsklare applikationer med omfattende platformunderstøttelse:

// app/layout.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'My Next.js App',
  description: 'Amazing Next.js application',

  // Omfattende favicon-konfiguration
  icons: {
    // Primære browserikoner
    icon: [
      { url: '/favicon-16x16.png', sizes: '16x16', type: 'image/png' },
      { url: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
    ],

    // Ældre ICO-understøttelse
    shortcut: '/favicon.ico',

    // iOS-startskærmsikoner
    apple: [
      { url: '/apple-touch-icon.png', sizes: '180x180', type: 'image/png' },
    ],

    // Android- og PWA-ikoner
    other: [
      {
        rel: 'icon',
        url: '/android-chrome-192x192.png',
        sizes: '192x192',
        type: 'image/png'
      },
      {
        rel: 'icon',
        url: '/android-chrome-512x512.png',
        sizes: '512x512',
        type: 'image/png'
      },
    ],
  },

  // PWA-manifest for app-lignende oplevelse
  manifest: '/site.webmanifest',

  // Yderligere mobiloptimering
  other: {
    'theme-color': '#000000',
    'msapplication-TileColor': '#000000',
  }
}

Metode 3: Dynamisk favicon-generering

Avanceret brug: Dynamiske favicons baseret på brugerkontekst, miljø eller applikationstilstand.

// app/layout.tsx
import { headers } from 'next/headers'
import type { Metadata } from 'next'

export async function generateMetadata(): Promise<Metadata> {
  const headersList = headers()
  const userAgent = headersList.get('user-agent') || ''

  // Miljøbaseret favicon-valg
  const isDevelopment = process.env.NODE_ENV === 'development'
  const isMobile = /Mobile|Android|iPhone/i.test(userAgent)

  // Dynamisk favicon-logik
  let faviconPath = '/favicon.ico'
  if (isDevelopment) {
    faviconPath = '/favicon-dev.ico' // Udviklingsindikator
  } else if (isMobile) {
    faviconPath = '/favicon-mobile.ico' // Mobiloptimeret version
  }

  return {
    title: 'My Next.js App',
    icons: {
      icon: faviconPath,
      apple: '/apple-touch-icon.png',
    },
    // Yderligere dynamisk metadata
    other: {
      'theme-color': isDevelopment ? '#ff6b6b' : '#000000',
    }
  }
}

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

Ældre Pages Router-implementering (Next.js 12 og ældre)

Metode 1: Komponentniveau-implementering med next/head

Bedst til: Sidespecifikke favicons, eller når du har brug for forskellige favicons til forskellige ruter.

// pages/_app.tsx
import Head from 'next/head'
import type { AppProps } from 'next/app'

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Head>
        <link rel="icon" href="/favicon.ico" />
        <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
        <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
        <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
        <link rel="manifest" href="/site.webmanifest" />
        <meta name="theme-color" content="#000000" />
      </Head>
      <Component {...pageProps} />
    </>
  )
}

Metode 2: Global implementering med Custom Document (anbefalet)

Bedst til: Applikationsdækkende favicon-konfiguration, der gælder for alle sider.

// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document'

export default function Document() {
  return (
    <Html lang="en">
      <Head>
        {/* Grundlæggende browserikoner */}
        <link rel="icon" href="/favicon.ico" />
        <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
        <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />

        {/* Mobilenhedsikoner */}
        <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
        <link rel="icon" type="image/png" sizes="192x192" href="/android-chrome-192x192.png" />
        <link rel="icon" type="image/png" sizes="512x512" href="/android-chrome-512x512.png" />

        {/* PWA- og platformkonfiguration */}
        <link rel="manifest" href="/site.webmanifest" />
        <meta name="theme-color" content="#000000" />
        <meta name="msapplication-TileColor" content="#000000" />
        <meta name="msapplication-config" content="/browserconfig.xml" />

        {/* Yderligere SEO- og mobiloptimering */}
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name="apple-mobile-web-app-status-bar-style" content="default" />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}

Avancerede implementeringer

Dynamiske favicon-opdateringer

Opret en brugerdefineret hook til dynamiske favicon-opdateringer:

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

export const useFavicon = (faviconUrl: string) => {
  useEffect(() => {
    const link = document.querySelector("link[rel*='icon']") as HTMLLinkElement ||
                 document.createElement('link')

    link.type = 'image/x-icon'
    link.rel = 'shortcut icon'
    link.href = faviconUrl

    if (!document.querySelector("link[rel*='icon']")) {
      document.getElementsByTagName('head')[0].appendChild(link)
    }
  }, [faviconUrl])
}

// Brug i komponent
export default function MyComponent() {
  const [theme, setTheme] = useState('light')

  useFavicon(theme === 'dark' ? '/favicon-dark.ico' : '/favicon-light.ico')

  return (
    <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
      Toggle Theme
    </button>
  )
}

Notifikationsbadge-favicon

Opret et notifikationssystem med favicon-badges:

// components/NotificationFavicon.tsx
import { useEffect, useRef } from 'react'

interface NotificationFaviconProps {
  count: number
  originalFavicon?: string
}

export const NotificationFavicon: React.FC<NotificationFaviconProps> = ({
  count,
  originalFavicon = '/favicon-32x32.png'
}) => {
  const canvasRef = useRef<HTMLCanvasElement>(null)

  useEffect(() => {
    const canvas = document.createElement('canvas')
    const ctx = canvas.getContext('2d')
    canvas.width = 32
    canvas.height = 32

    const img = new Image()
    img.onload = () => {
      if (!ctx) return

      // Tegn originalt favicon
      ctx.drawImage(img, 0, 0, 32, 32)

      if (count > 0) {
        // Tegn notifikationsbadge
        ctx.fillStyle = '#ff4444'
        ctx.beginPath()
        ctx.arc(24, 8, 8, 0, 2 * Math.PI)
        ctx.fill()

        // Tegn tællertekst
        ctx.fillStyle = 'white'
        ctx.font = 'bold 10px Arial'
        ctx.textAlign = 'center'
        ctx.textBaseline = 'middle'
        ctx.fillText(count > 9 ? '9+' : count.toString(), 24, 8)
      }

      // Opdater favicon
      const link = document.querySelector("link[rel*='icon']") as HTMLLinkElement ||
                   document.createElement('link')
      link.type = 'image/png'
      link.rel = 'shortcut icon'
      link.href = canvas.toDataURL()

      if (!document.querySelector("link[rel*='icon']")) {
        document.getElementsByTagName('head')[0].appendChild(link)
      }
    }

    img.src = originalFavicon
  }, [count, originalFavicon])

  return null
}

// Brug
export default function App() {
  const [notifications, setNotifications] = useState(0)

  return (
    <>
      <NotificationFavicon count={notifications} />
      <button onClick={() => setNotifications(notifications + 1)}>
        Tilføj notifikation ({notifications})
      </button>
    </>
  )
}

Tema-adaptivt favicon

Implementer favicon der tilpasser sig systemtema:

// components/ThemeAdaptiveFavicon.tsx
import { useEffect, useState } from 'react'

export const ThemeAdaptiveFavicon = () => {
  const [isDark, setIsDark] = useState(false)

  useEffect(() => {
    // Tjek systempræference
    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
    setIsDark(mediaQuery.matches)

    // Lyt efter ændringer
    const handleChange = (e: MediaQueryListEvent) => {
      setIsDark(e.matches)
    }

    mediaQuery.addEventListener('change', handleChange)
    return () => mediaQuery.removeEventListener('change', handleChange)
  }, [])

  useEffect(() => {
    const faviconUrl = isDark ? '/favicon-dark.ico' : '/favicon-light.ico'

    const link = document.querySelector("link[rel*='icon']") as HTMLLinkElement ||
                 document.createElement('link')

    link.type = 'image/x-icon'
    link.rel = 'shortcut icon'
    link.href = faviconUrl

    if (!document.querySelector("link[rel*='icon']")) {
      document.getElementsByTagName('head')[0].appendChild(link)
    }
  }, [isDark])

  return null
}

// Brug i _app.tsx eller layout.tsx
export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <ThemeAdaptiveFavicon />
      <Component {...pageProps} />
    </>
  )
}

Web Manifest-konfiguration

Opret et komplet web manifest til PWA-understøttelse:

// public/site.webmanifest
{
  "name": "My Next.js App",
  "short_name": "NextApp",
  "description": "Amazing Next.js application",
  "icons": [
    {
      "src": "/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "theme_color": "#000000",
  "background_color": "#ffffff",
  "display": "standalone",
  "start_url": "/",
  "scope": "/"
}

Build-time favicon-generering

Automatisér favicon-generering under build:

// scripts/generate-favicons.js
const sharp = require('sharp')
const fs = require('fs')

const sizes = [
  { size: 16, name: 'favicon-16x16.png' },
  { size: 32, name: 'favicon-32x32.png' },
  { size: 180, name: 'apple-touch-icon.png' },
  { size: 192, name: 'android-chrome-192x192.png' },
  { size: 512, name: 'android-chrome-512x512.png' }
]

async function generateFavicons() {
  const inputFile = 'assets/logo.png'

  for (const { size, name } of sizes) {
    await sharp(inputFile)
      .resize(size, size)
      .png()
      .toFile(`public/${name}`)

    console.log(`Generated ${name}`)
  }

  // Generér ICO-fil
  await sharp(inputFile)
    .resize(32, 32)
    .toFile('public/favicon.ico')

  console.log('Generated favicon.ico')
}

generateFavicons().catch(console.error)
// package.json
{
  "scripts": {
    "generate-favicons": "node scripts/generate-favicons.js",
    "build": "npm run generate-favicons && next build"
  }
}

Almindelige problemer og løsninger

Problem 1: Favicon opdateres ikke under udvikling

Problem: Browseren cacher gammelt favicon under udvikling

Løsning:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  async headers() {
    return [
      {
        source: '/favicon.ico',
        headers: [
          {
            key: 'Cache-Control',
            value: process.env.NODE_ENV === 'development'
              ? 'no-cache, no-store, must-revalidate'
              : 'public, max-age=31536000, immutable',
          },
        ],
      },
    ]
  },
}

module.exports = nextConfig

Problem 2: Favicon mangler i produktion

Problem: Statiske filer serveres ikke korrekt

Løsning:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  async rewrites() {
    return [
      {
        source: '/favicon.ico',
        destination: '/favicon.ico',
      },
    ]
  },
}

module.exports = nextConfig

Problem 3: Flere favicon-formater indlæses ikke

Problem: Kompleks favicon-opsætning forårsager konflikter

Løsning: Brug en prioritetsbaseret tilgang:

// components/FaviconManager.tsx
import Head from 'next/head'

export const FaviconManager = () => {
  return (
    <Head>
      {/* Høj prioritet: Moderne browsere */}
      <link rel="icon" type="image/svg+xml" href="/favicon.svg" />

      {/* Medium prioritet: PNG-fallback */}
      <link rel="icon" type="image/png" href="/favicon-32x32.png" />

      {/* Lav prioritet: Ældre ICO */}
      <link rel="shortcut icon" href="/favicon.ico" />

      {/* Mobilspecifik */}
      <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
      <link rel="manifest" href="/site.webmanifest" />
    </Head>
  )
}

Test af din favicon-implementering

Udviklingstjekliste

  • [ ] Favicon vises i browserfaner
  • [ ] Favicon vises i bogmærker
  • [ ] Mobil "Føj til startskærm" fungerer
  • [ ] PWA-installationsikon er korrekt
  • [ ] Mørk/lys tilstand-tilpasning (hvis implementeret)

Professionelle testværktøjer

1. Favicon.im - Hurtig validering

  • Hurtig favicon-udtrækning og test
  • Kompatibilitetstjek på tværs af platforme
  • Identifikation af manglende størrelser
  • Bedst til: Hurtig validering og fejlfinding

2. RealFaviconGenerator Checker - Omfattende analyse

  • Detaljeret platformspecifik test
  • PWA-overensstemmelsesverifikation
  • Ydelsesanbefalinger
  • Bedst til: Professionelle audits og optimering

3. Browser DevTools - Teknisk fejlsøgning

  • Netværksfanen til indlæsningsproblemer
  • Konsolfejl for manglende filer
  • Applikationsfanen til manifest-inspektion
  • Bedst til: Teknisk fejlfinding og ydelsesanalyse

Manuelle testtrin

  1. Ryd browsercache
  2. Besøg din side i inkognitotilstand
  3. Test på forskellige enheder
  4. Bekræft bogmærkeudseende
  5. Test "Føj til startskærm"-funktionalitet

Ydelsesoptimering

Filstørrelsesoptimering

# Optimér PNG-filer
pngquant --quality=65-80 --output favicon-optimized.png favicon.png

# Optimér ICO-filer
convert favicon.png -resize 32x32 -colors 256 favicon.ico

HTTP-cachingheadere

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  async headers() {
    return [
      {
        source: '/:path(favicon.ico|.*\\.png)',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=31536000, immutable',
          },
        ],
      },
    ]
  },
}

module.exports = nextConfig

Komplet implementeringstjekliste

Fase 1: Grundlæggende opsætning

  • [ ] Generér favicon-filer med RealFaviconGenerator eller Favicon.io
  • [ ] Placer filer i public-mappen med korrekt navngivning
  • [ ] Vælg implementeringsmetode (App Router vs Pages Router)
  • [ ] Grundlæggende HTML-opsætning med nødvendige link-tags
  • [ ] Test grundlæggende funktionalitet på tværs af større browsere

Fase 2: Multi-enhedsoptimering

  • [ ] iOS-startskærmsunderstøttelse (180x180 apple-touch-icon)
  • [ ] Android-kompatibilitet (192x192 og 512x512 ikoner)
  • [ ] PWA manifest-konfiguration for app-lignende oplevelse
  • [ ] Windows tile-understøttelse med korrekte meta-tags
  • [ ] Temafarveintegration for mobilbrowsere

Fase 3: Avancerede funktioner (valgfrit)

  • [ ] Dynamiske favicon-opdateringer med brugerdefinerede hooks
  • [ ] Tema-adaptive ikoner til lys/mørk tilstand
  • [ ] Notifikationsbadges til realtidsopdateringer
  • [ ] Ydelsesoptimering med cachingheadere
  • [ ] Build-time generering med automatiserede scripts

Vigtige implementeringsstrategier

Til moderne Next.js-projekter (13+)

Anbefalet: Brug App Router med metadata.icons API for typesikker, optimeret favicon-håndtering.

Til ældre projekter (12 og ældre)

Anbefalet: Implementer i _document.tsx for global dækning med next/head for sidespecifikke behov.

Til dynamiske applikationer

Avanceret: Kombinér statisk opsætning med runtime-opdateringer ved hjælp af brugerdefinerede hooks og canvas-manipulation.

Til PWA-applikationer

Essentielt: Inkludér omfattende manifest-konfiguration med flere ikonstørrelser og korrekte meta-tags.

Afsluttende anbefalinger

Start enkelt: Begynd med grundlæggende ICO + PNG-opsætning, og udvid derefter baseret på behov

Brug professionelle værktøjer: RealFaviconGenerator for omfattende dækning

Test grundigt: Validér på tværs af browsere, enheder, og brug Favicon.im til hurtig test

Optimér ydeevne: Implementér korrekte cachingheadere og komprimer favicon-filer

Planlæg for vækst: Design dit favicon-system til at kunne rumme fremtidige funktioner som notifikationer og tematilpasning

Ved at følge denne omfattende guide vil du skabe et professionelt favicon-system, der forbedrer brugeroplevelsen, styrker brandgenkendelse og fungerer gnidningsfrit på tværs af alle moderne enheder og browsere.

Check Your Favicon

Use favicon.im to quickly check if your favicon is configured correctly. Our free tool ensures your website's favicon displays properly across all browsers and devices.

Free Public Service

Favicon.im is a completely free public service trusted by developers worldwide.

15M+
Monthly Favicon Requests
100%
Free Forever