Elementary Standards
HTMLCSSWeb StandardsSEOBlogStart reading
Home / Blog / The Web App Manifest and PWA Installability: A Developer's Guide
HTML

The Web App Manifest and PWA Installability: A Developer's Guide

What actually makes a PWA installable in 2026 — and the manifest mistakes that quietly break it.

11 min read·2026-11-04·

Open the address bar on a reasonably well-built progressive web app and Chrome, Edge, or Samsung Internet offers to install it — sometimes a small icon, sometimes a banner, and occasionally nothing at all even though you were certain the site qualified. That gap between “should be installable” and “actually shows the prompt” is almost always a manifest problem, not a browser mood swing. Each engine runs a checklist against your manifest.json and your service worker, and it fails that checklist silently, with no console warning pointing at the cause.

The manifest itself is small — a JSON file with a dozen possible fields — but a handful of them are load-bearing. Get the icon sizes wrong, omit start_url, or leave display on its browser default, and the install affordance just doesn't appear. Nothing crashes. Nothing logs an error. The page keeps working fine as a tab; it simply never becomes an app.

When a product outgrows what the manifest and a service worker can offer — background geofencing, Bluetooth peripherals, push notifications that behave the same way on iOS as on Android — the gap is usually closed by going native, which is where mobile app development services pick up where a PWA's manifest leaves off. Most products never need that jump, though, if the manifest and the install criteria are actually correct, so that's the part worth getting right first.

TL;DR: A PWA installs when its manifest has a name, 192px and 512px icons (one marked maskable), a start_url, a non-browser display mode, is served over HTTPS, and ships a service worker with a fetch handler. Chrome and Edge prompt automatically once those criteria are met; Safari and Firefox require a manual “Add to Home Screen,” but now pass the same Lighthouse PWA audit. Most install failures trace back to a mismatched icon purpose or an inconsistent manifest URL.

install

What the web app manifest actually does

A web app manifest is a JSON file — conventionally manifest.json or manifest.webmanifest — linked from the page with <link rel="manifest" href="/manifest.json">. It doesn't run any code. It's pure metadata: the name the OS should show, which icons represent the app at which sizes, what color to paint the splash screen and status bar, which orientation the app prefers, and — critically for this article — how much browser chrome should remain visible once the app is launched from a home screen or app drawer.

That split matters. The manifest describes how the app should look once installed; the service worker is what makes it work offline and qualifies it for installation in the first place. Browsers treat the two as separate, mandatory ingredients of the same recipe — a flawless manifest with no service worker won't install, and a bulletproof service worker with a broken manifest won't either.

The fields that make or break installability

Chromium's installability checklist only cares about a handful of manifest members, and most failures come from getting one of these subtly wrong rather than omitting it outright:

A minimal manifest that satisfies every one of those is short enough to hand-write:

{
  "name": "Field Notes",
  "short_name": "FieldNotes",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#0d111b",
  "theme_color": "#0098f2",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
  ]
}

For the full, authoritative field list — including the newer id, scope, and shortcuts members — MDN's manifest reference is the source worth bookmarking; it's updated as browsers add fields faster than most blog posts can track.

Icons and the maskable trap

The 192px and 512px sizes aren't arbitrary. Android uses the smaller one for the app drawer and task switcher and the larger one for the splash screen Chrome generates while the app boots. Supply only one size and the browser will scale it — usually badly, with visible compression artifacts on the splash screen.

The trap is purpose: "maskable". Android applies adaptive icon masks — circle, squircle, rounded square, depending on the device's launcher — and clips whatever falls outside a safe zone roughly 80% of the icon's width. An icon designed for a square frame, marked maskable without redrawing it to sit inside that safe zone, gets its logo or text clipped at the edges on real devices, even though it previews perfectly in a design tool. The fix is to either supply a dedicated maskable icon with generous padding, or omit purpose: "maskable" entirely and let the browser apply its own background-and-pad fallback — which looks plainer but never clips content.

Display modes compared

The four display values aren't a quality ladder — each trades browser affordances for immersion, and the right one depends on what the app needs the user to be able to do without leaving it:

display valueWhat the user seesUse it when
standaloneNo browser UI, but the OS status bar and back gesture remainThe default choice for almost any installable app
fullscreenThe status bar is hidden too — the app owns the entire screenGames and immersive media; rarely anything else
minimal-uiA trimmed browser chrome with back and reload controls visibleContent apps that still want a visible reload affordance
browserA normal browser tab — no install prompt is offered at allNever, for an app you actually want installed; it's the value that disqualifies you

Most teams should default to standalone and only reach for fullscreen when the status bar actively competes with the content for the same pixels.

HTTPS and the other non-negotiables

Three requirements sit underneath the manifest fields and get almost no attention because they're binary: either you clear them or the browser won't even evaluate your manifest.

The page must be served over HTTPS, with localhost treated as an exception for local development. A service worker must be registered and successfully controlling the page's scope — a worker file that throws during installation, or one scoped to a subpath that doesn't cover start_url, counts as not having one. And the manifest must be fetched from the same origin as the document that links to it; a manifest served from a CDN subdomain, even with permissive CORS headers, fails the same-origin check that installability relies on.

Why won't Chrome show the install prompt?

This is the practical checklist — the one worth running before you file a browser bug, because the cause is almost always local:

Customizing beforeinstallprompt without breaking the UX

Chromium browsers fire a beforeinstallprompt event before showing their own install banner, and calling event.preventDefault() on it lets you defer that banner and trigger it later from a button inside your own UI — a pattern most teams reach for once the generic browser banner feels disconnected from the rest of the product.

let deferredPrompt;

window.addEventListener('beforeinstallprompt', (event) => {
  event.preventDefault();
  deferredPrompt = event;
  showOwnInstallButton();
});

installButton.addEventListener('click', async () => {
  if (!deferredPrompt) return;
  deferredPrompt.prompt();
  const { outcome } = await deferredPrompt.userChoice;
  deferredPrompt = null;
  hideOwnInstallButton();
});

The event can only be replayed once — call .prompt() a second time on the same captured event and it's a no-op. The other common mistake is firing the custom button immediately on page load: Chrome's own engagement heuristics (repeat visits, time on site) exist precisely to stop sites from nagging first-time visitors, and routing around them with your own UI the moment the page loads defeats the point and annoys users who haven't decided anything yet. Safari doesn't fire beforeinstallprompt at all — there's no deferred-prompt pattern to build there, only the manual “Add to Home Screen” flow.

Browser support in 2026

Chrome and Edge remain the most complete implementation: automatic installability evaluation, the beforeinstallprompt event, and desktop installation alongside mobile. Firefox supports installable PWAs on desktop, with more limited exposure on Android, and doesn't fire beforeinstallprompt either — it surfaces its own install affordance in the address bar when the manifest qualifies.

Safari still has no automatic install banner and no beforeinstallprompt equivalent; users add a site to the home screen manually through the share sheet. What's changed this year is that Safari now evaluates manifest validity closely enough to pass a Lighthouse PWA installability audit when the same criteria used for Chrome are met — a meaningful shift from the years when Safari's manifest support was too partial to score cleanly on the same checklist at all. It still won't prompt automatically, but a correctly built manifest is no longer a Chrome-only investment.

Testing installability before you ship

Chrome DevTools' Application panel has a dedicated Manifest tab that parses your file and flags missing or malformed fields directly, including icon and installability warnings that previously only surfaced in the separate Issues tab — we covered that diagnostic surface in more detail in an earlier piece on Chrome's manifest diagnostics. Running a Lighthouse PWA audit (built into DevTools, or via the CLI) is the fastest second opinion: it re-runs the same installability checklist the browser itself uses and lists every failing criterion by name rather than making you guess from a missing icon.

Test on a real device where possible. Simulators get the manifest parsing right but can mask icon-masking issues and splash-screen scaling that only show up on an actual Android launcher.

Frequently Asked Questions

Do I need a service worker to be installable?

Yes — Chromium-based browsers require a registered service worker with at least a no-op fetch event handler as part of the installability checklist; a manifest alone, without a controlling service worker, won't trigger the prompt.

Can I force the install prompt to appear?

Not directly. You can capture beforeinstallprompt and replay it from your own UI, but you can't make the event fire before the browser's engagement heuristics — repeat visits, time on site — decide the user is a plausible installer.

Why does my manifest.json 404 on some pages but not others?

Usually a relative <link rel="manifest"> path that resolves differently depending on the current URL's depth. Use a root-relative path like /manifest.json, never a bare manifest.json.

Does PWA installability affect SEO?

Not as a direct ranking factor, but an installed PWA with offline support and re-engagement tends to improve the return-visit and session-depth signals that correlate with better organic performance over time.

What's the difference between name and short_name?

name is the full app name shown on the install dialog and in app listings; short_name is a truncated version — keep it under about 12 characters — used under the home-screen icon where space is tight.

Does the manifest need to be on the same origin as the page?

Yes. Browsers refuse to evaluate installability if the manifest URL isn't same-origin with the document linking to it, even when CORS headers are configured correctly.

Can a PWA installed in Chrome also show up in Safari?

No. Installation is per-browser and per-OS. A user who installs your PWA through Chrome on Android gets no equivalent install on an iPhone unless they separately add it to the home screen in Safari.

None of this is complicated once it's written down — it's just unforgiving about small mismatches between what the manifest claims and what the files on disk actually contain. Get the fields right, keep the manifest and service worker scope aligned, and the install prompt stops being a mystery and starts being a checklist you can tick off before every release.

Back to the blog
More notes

Keep reading