social

Web app manifest

MetricSpot checks for a linked web app manifest. It's required for Android install prompts, the tab-strip icon, and modern PWA behavior across Chromium browsers.

What this check does

Looks for <link rel="manifest" href="..."> in the document head, fetches the referenced JSON file, and validates that it parses and contains the fields browsers require to show an install prompt: name (or short_name), start_url, icons with at least one 192×192 and one 512×512, and display.

Why it matters

The manifest controls how the operating system treats your site once a user adds it to their home screen.

  • Android (Chrome, Samsung Internet, Edge): with a valid manifest plus a service worker, the browser shows the “Add to Home Screen” prompt. Without it, no install banner — users have to find the hidden menu option.
  • Chromebook / desktop Chrome / Edge: installable as a standalone app from the address bar omnibox icon.
  • iOS Safari: reads name, theme_color, and apple-touch-icon (still a separate link). Doesn’t honor display: standalone from the manifest — you also need <meta name="apple-mobile-web-app-capable" content="yes">.
  • Tab strip: Chrome uses theme_color from the manifest (and the matching meta tag) for the address-bar background color.

Even if you don’t want a PWA install prompt, the manifest still feeds the favicon used by Android when users add a bookmark, and the icon Chrome shows in the tab overflow menu on mobile.

How to fix it

1. Link the manifest from every page:

<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#0b0b0b" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />

The file extension is conventionally .webmanifest, but .json works too as long as the response is served with Content-Type: application/manifest+json (or application/json).

2. Write a minimal valid manifest:

{
  "name": "MetricSpot",
  "short_name": "MetricSpot",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#0b0b0b",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "any"
    },
    {
      "src": "/icons/maskable-512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "maskable"
    }
  ]
}

The maskable icon is the one Android uses for the home-screen badge. Without it, Android crops your icon and you get a circle of white space around a logo. Test with maskable.app.

3. Serve the right content type:

location = /manifest.webmanifest {
  default_type application/manifest+json;
  add_header Cache-Control "public, max-age=3600";
}

If your host serves it as text/plain or text/html, Chrome ignores the file silently.

Astro: drop manifest.webmanifest into public/ — Astro copies it to the site root unchanged. Add the <link> to your base layout.

Next.js (App Router): export a typed manifest from app/manifest.ts and Next emits the JSON at build time:

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

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

The <link rel="manifest"> is injected automatically by Next when this file exists.

WordPress: the simplest path is a PWA plugin (PWA by 10up, or Super Progressive Web Apps). Or write the manifest yourself, drop it in the theme’s root, and add the <link> tag via wp_head:

add_action('wp_head', function () {
  echo '<link rel="manifest" href="' . esc_url(get_template_directory_uri() . '/manifest.webmanifest') . '">';
});

4. Pair with the right meta tags. The manifest does most of the work, but iOS Safari and legacy crawlers still want the separate tags:

<meta name="theme-color" content="#0b0b0b" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />

See also Apple touch icon and Favicon for the rest of the icon set.

Frequently asked questions

Do I need a service worker to pass this check?

No. The manifest check only verifies the <link> and the JSON file. You need a service worker to be installable as a PWA on Android — without one, the install prompt won’t show even with a perfect manifest. The manifest alone still gives you a better home-screen icon and tab-strip metadata.

Why isn’t my install prompt showing on Android?

Three things are required: a valid manifest with 192×192 and 512×512 icons, a service worker that handles fetch events, and the site served over HTTPS. Check chrome://inspect → “Devices” → your tab → Application → Manifest in DevTools — Chrome lists exactly which install criteria are missing.

What’s the difference between display: standalone and display: fullscreen?

standalone hides the browser chrome but keeps the OS status bar (notifications, time, battery) — what most apps want. fullscreen hides everything and is usually only appropriate for games. minimal-ui keeps a thin browser bar with back/forward; rarely used. Most installable apps want standalone.

Sources

Last updated 2026-05-11