Web Assets

Favicon and PWA assets for a modern website, with optional Legacy Browser Support.

Modern-first export: covers current browsers, PWAs, and the iOS Add-to-Home-Screen surface out of the box. Legacy Browser Support is opt-in.

Default set

your-app/public/
├── favicon.ico              # 16, 32, 48 — legacy + implicit /favicon.ico probe
├── icon.svg                 # vector — modern tabs, hi-DPI
├── icon-192.png             # PWA install icon
├── icon-512.png             # large PWA icon / splash
├── icon-512-maskable.png    # Android adaptive (full-bleed, safe-zone padded)
├── apple-touch-icon.png     # 180 — iOS Add to Home Screen
├── manifest.webmanifest
└── SETUP.md

If your design uses a built-in icon from Lucide, the ZIP may also include ATTRIBUTION.txt.

Paste this into <head>

<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" type="image/svg+xml" href="/icon.svg">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#ffffff">

Order matters: browsers pick the last matching rel="icon" they support, so SVG comes after ICO.

For per-scheme browser chrome colors, split theme-color:

<meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff">
<meta name="theme-color" media="(prefers-color-scheme: dark)"  content="#000000">

Next.js (App Router)

// app/layout.tsx
export const metadata = {
  icons: {
    icon: [
      { url: '/favicon.ico', sizes: 'any' },
      { url: '/icon.svg', type: 'image/svg+xml' },
    ],
    apple: '/apple-touch-icon.png',
  },
  manifest: '/manifest.webmanifest',
};

manifest.webmanifest

The shipped manifest is a stub — edit name, short_name, and theme_color to match the app:

{
  "name": "Your App Name",
  "short_name": "App",
  "icons": [
    { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" },
    {
      "src": "/icon-512-maskable.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ],
  "theme_color": "#ffffff",
  "background_color": "#ffffff",
  "display": "standalone"
}

Maskable icon

icon-512-maskable.png is generated from your design with three forced adjustments so it survives Android's adaptive-icon mask:

  • Padding is zeroed (background fills edge to edge).
  • Artwork is scaled to fit the inner 80% safe zone (W3C requirement).
  • Border radius is removed (the OS supplies the clip shape).

The source design is untouched; these only apply at export time.

Legacy Browser Support (opt-in)

Toggle this on if the project still needs IE / old Edge pinned-tile support or explicit small PNG fallback files. Adds:

FileWhen
icon-16.png, icon-32.pngBrowsers that ignore ICO
mstile-150x150.png + browserconfig.xmlLegacy Windows pinned tiles

For most new projects, skip this — favicon.ico already handles legacy browsers that probe for it.

Compatibility map

SurfaceAsset
Modern browser tabicon.svg
Legacy browser tabfavicon.ico
iOS Add to Home Screenapple-touch-icon.png
PWA install promptmanifest.webmanifest + icon-192.png, icon-512.png
Android adaptive iconicon-512-maskable.png
Legacy Windows pinned sitesbrowserconfig.xml, mstile-150x150.png (opt-in)

Validate after install

Run these checks on the deployed site, not only in the editor.

1. Files are reachable

Open each URL directly in the browser (replace host with yours):

https://your-site.com/favicon.ico
https://your-site.com/icon.svg
https://your-site.com/apple-touch-icon.png
https://your-site.com/manifest.webmanifest
https://your-site.com/icon-192.png
https://your-site.com/icon-512.png

Each should return 200 with the expected content type.

DevTools → Elements → inspect <head>:

  • Two rel="icon" entries (ICO + SVG) in that order
  • rel="apple-touch-icon" pointing at /apple-touch-icon.png
  • rel="manifest" pointing at /manifest.webmanifest
  • meta name="theme-color" matching your manifest stub (edit both to your brand color)

3. Manifest parses

DevTools → ApplicationManifest:

  • No parse errors
  • Icons lists 192, 512, and maskable 512 with correct paths
  • Edit name, short_name, and theme_color in the shipped stub before shipping

4. Favicon in the tab

Hard refresh (⌘+Shift+R / Ctrl+Shift+R) or use a private window. Browsers cache favicons independently of HTML cache — seeing the old icon after a successful deploy is common.

5. Cache while iterating

During local development, append a query string to the SVG link:

<link rel="icon" type="image/svg+xml" href="/icon.svg?v=2">

Bump the version when you replace the file. Remove or stabilize the query param for production once assets are final.

Manifest serving

manifest.webmanifest must be served as application/manifest+json or application/json. Next.js and Vercel do this by default. Static hosts sometimes serve .webmanifest as application/octet-stream — override the MIME type if the Application panel reports a parse failure.

Icon not updating, PWA icon missing, manifest errors — see Troubleshooting.