Next.js handles favicons differently depending on whether you use the App Router (app/) or the Pages Router (pages/). This guide covers both, plus the file conventions that let Next.js generate your <link> tags automatically.
The fastest method: use Next.js file conventions
In the App Router, Next.js auto-detects special filenames in your app/ directory and injects the right tags into <head> with no code.
Place any of these files directly in app/:
| File | What it does |
|---|---|
favicon.ico | The classic .ico — universal fallback |
icon.png / icon.svg | Modern icon; Next.js sizes it automatically |
apple-icon.png | iOS / Safari touch icon |
app/
├── favicon.ico # universal fallback
├── icon.png # main icon (≥512×512)
├── icon.svg # optional, crisp vector
├── apple-icon.png # 180×180 for iOS
└── layout.tsxThat's it — no manual <link> tags required. Next.js will emit the correct markup at build time.
Need all those files generated from one image? Upload it to Favicon.one and download the full package ready for the
app/directory.
Adding extra sizes and a manifest
For a complete PWA setup (Android, splash screens, multiple PNG sizes), generate the files and add a metadata-based icon config in layout.tsx:
import type { Metadata } from 'next'
export const metadata: Metadata = {
icons: {
icon: [
{ url: '/favicon.ico', sizes: 'any' },
{ url: '/icon.svg', type: 'image/svg+xml' },
{ url: '/favicon-32x32.png', sizes: '32x32', type: 'image/png' },
{ url: '/favicon-16x16.png', sizes: '16x16', type: 'image/png' },
],
apple: ['/apple-touch-icon.png'],
},
manifest: '/site.webmanifest',
}Pages Router (legacy)
If you're on the Pages Router, there are no auto-detected icon files. Put your files in public/ and add the tags to pages/_document.tsx:
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang="en">
<Head>
<link rel="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" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}Common Next.js favicon pitfalls
1. The favicon shows in dev but not in production
Usually a build cache issue. Delete .next and rebuild. Also confirm the file is actually copied to the output — check .next/static or your deployment.
2. Using a non-square image
Favicons must be square. Crop your source image first, or the icon will look stretched. The Favicon.one generator handles cropping and resizing for you.
3. Next.js caching stale favicons
Next.js aggressively hashes static assets. After replacing a favicon, do a hard rebuild and redeploy — and tell users to hard-refresh.
4. Putting favicon files in the wrong folder
App Router: files go in app/. Pages Router: files go in public/. Mixing these up is the most common Next.js favicon mistake.
Verify it worked
After deploying, audit your site with the Favicon Checker — it confirms every required icon and tag is present and correctly referenced.
Want the full favicon file package with the correct Next.js structure? Generate it free at Favicon.one.