Skip to content

Social & sharing · Next.js (App Router)

How to add a web app manifest in Next.js (App Router)

The manifest decides how your site looks when someone saves it to a home screen: without one they get a screenshot thumbnail and the raw URL as the label. A manifest link pointing at nothing is worse than none — the browser requests it on every first visit and gets an error.

The fix for Next.js (App Router)

13x detects your framework from the response and hands you this version rather than the generic one — below 50% confidence it hedges and gives you the generic one instead.

Generate it from a typed file so the icon paths stay checked:

ts code
// app/manifest.ts
import type { MetadataRoute } from "next";

export default function manifest(): MetadataRoute.Manifest {
  return {
    name: "example.com",
    short_name: "example.com",
    start_url: "/",
    display: "standalone",
    background_color: "#0b0b12",
    theme_color: "#0b0b12",
    icons: [
      { src: "/icon-192.png", sizes: "192x192", type: "image/png" },
      { src: "/icon-512.png", sizes: "512x512", type: "image/png" },
      {
        src: "/icon-maskable.png",
        sizes: "512x512",
        type: "image/png",
        purpose: "maskable",
      },
    ],
  };
}

Next serves this at /manifest.webmanifest and injects the <link> itself — do not also add one by hand.

theme-color is separate, and belongs in the root layout's viewport export rather than its metadata export:

ts code
// app/layout.tsx
import type { Viewport } from "next";

export const viewport: Viewport = {
  themeColor: "#0b0b12",
};

On a different stack? The general version of this fix explains what 13x checks and why it matters, without assuming a framework.

Check your Next.js (App Router) site

113 deterministic checks against your live URL, in about 30 seconds. Framework detected from the response, so every fix comes back in the form your stack actually uses.

No signup. Results in 30 seconds.