skip to content

PWA: Manifest, Install, Push, and Background Sync

You will learn what upgrades a site to an installable app: a manifest, installability criteria, and the background capabilities a service worker unlocks. Interviewers ask this when the role touches mobile-web or offline field usage.

on this pageshow

questions

6

What is the web app manifest, and which of its fields does the browser use to decide an installed app's launch URL, window appearance, and icon?

level: juniorimportance: should knowfreq 58%

answer

  1. JSON metadata, read only at install
  2. launch URL and its boundary
  3. window chrome versus plain tab
  4. two icon sizes plus maskable
  5. identity separate from the launch URL

basics

~20 s

A web app manifest is a JSON file that describes a site as an installable app. start_url sets the launch URL, scope bounds which pages stay in the app window, display picks standalone or browser chrome, and icons supply launcher artwork.

solid answer

~40 s

The manifest is a JSON document the browser reads when it evaluates or performs an install. Identity comes from `name` (install dialog), `short_name` (under the home-screen icon) and optionally `id`, which pins the app's identity so changing the launch URL does not create a second installed app. `start_url` is what opens when the user taps the icon; `scope` is a URL prefix, and navigations outside it are handed back to the normal browser instead of staying in the app window. `display` chooses the window: `standalone`, `minimal-ui`, `fullscreen`, or `browser` for a plain tab. `icons` is an array of `{src, sizes, type, purpose}`; Chromium wants a 192px and a 512px icon, and `purpose: "maskable"` supplies the safe-zone variant Android crops. `theme_color` and `background_color` colour the title bar and the launch splash.

code

json · 16 lines
json
{
  "id": "/?app",
  "name": "Fieldwork Reports",
  "short_name": "Fieldwork",
  "start_url": "/?source=pwa",
  "scope": "/",
  "display": "standalone",
  "display_override": ["window-controls-overlay", "standalone"],
  "theme_color": "#12263a",
  "background_color": "#ffffff",
  "icons": [
    { "src": "/icons/192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/512.png", "sizes": "512x512", "type": "image/png" },
    { "src": "/icons/maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
  ]
}

go deeper

for a junior

Be able to name the file, say it is JSON, and list what start_url, scope, display and icons each do. Interviewers want the vocabulary, not the edge cases.

for a middle

Explain how scope decides which navigations stay in the app window, why display_override exists as a fallback chain, and why background_color matters for the launch splash.

for a senior

Show you have debugged a real install: an app duplicated because start_url changed without a stable id, users bounced out of the window by a too-narrow scope, or a clipped logo from a missing maskable icon.

for a principal

Own the decision of what belongs in scope at all — auth redirects, third-party checkout and marketing pages each argue for a different boundary, and getting it wrong shows up as users leaving the installed experience without noticing.

## What the manifest is A web app manifest is a JSON document, served with the media type `application/manifest+json`, that tells the browser how to present a site when it is installed as an app. The document references it, and the browser fetches it when it evaluates installability or when the user installs. It changes nothing about how the page renders in an ordinary tab — a manifest with a typo does not break the site, it just quietly stops the install from working. ## Identity: name, short_name, id `name` is the full application name shown in the install dialog and app lists. `short_name` is the truncated label shown under a home-screen icon, so it should be a handful of characters. `id` is an explicit identity string for the app. Without it, the browser derives identity from `start_url`, which means that changing the launch URL later can be interpreted as a *different* app and produce a duplicate install rather than an update. Setting `id` once (`"id": "/?app"`, for example) makes identity independent of the launch URL. ## Launch and boundaries: start_url and scope `start_url` is resolved relative to the manifest's own URL and is what opens when the icon is tapped. Teams frequently add a marker query — `"start_url": "/?source=pwa"` — so analytics can separate launches from ordinary web traffic. `scope` is a URL prefix defining what counts as "inside" the app. Navigations within scope stay in the app window; a navigation outside it (an external link, a payment provider, an auth redirect on another origin) is handed to the browser, typically as an in-app browser view. If `scope` is omitted it defaults to `start_url` with its last path segment removed, which is why an app whose `start_url` is `/app/index.html` and whose real routes live at `/` can mysteriously bounce users out to a browser tab. ## Presentation: display, theme_color, background_color `display` takes one of four values, and the browser falls back down the list if it cannot honour the one you asked for: - `fullscreen` — no browser UI and no system UI where the platform allows it. - `standalone` — its own window, app title bar, no URL bar. - `minimal-ui` — standalone plus a minimal set of navigation controls. - `browser` — a normal tab; Chromium also treats this as not installable. `display_override` is an ordered array consulted before `display`, and is how you opt into newer modes such as `"window-controls-overlay"` while keeping `standalone` as the fallback. `theme_color` tints the app's title bar and the OS task-switcher entry; `background_color` paints the launch screen in the moment between tapping the icon and your first paint, so it should match your app's real background or users see a flash. In script you can detect the mode you were launched in: ```js const installed = window.matchMedia('(display-mode: standalone)').matches; ``` ## Icons `icons` is an array of objects with `src`, `sizes` (`"192x192"`, or `"any"` for SVG), `type`, and `purpose`. `purpose: "any"` is the default; `purpose: "maskable"` marks an icon drawn with a safe zone so Android can crop it into whatever shape the launcher uses — without one, an adaptive launcher may clip your logo. `purpose: "monochrome"` supplies a single-colour variant for badging. ## What the manifest does not do The manifest is metadata only. It does not cache anything, does not make the app work offline, and does not intercept requests — those are jobs for a service worker. Nor is any of it required for the site to function; a site without a manifest is simply a site that cannot be installed as an app. ## Debugging Browser devtools expose the parsed manifest, so the fastest check is to open the application panel and look at what the browser actually resolved: a manifest fetched with the wrong content type, blocked by an auth gate, or containing trailing-comma JSON produces an empty panel rather than an error in the console.

  • A user taps a link to an external payment provider from inside your installed app. What does the browser do with that navigation, and what controls it?
    The URL falls outside `scope`, so the browser takes the navigation out of the app window and shows it in an in-app browser view with visible origin information. That is deliberate: the standalone window has no URL bar, so the platform refuses to render a foreign origin inside it. Widening `scope` cannot cover another origin — scope is same-origin only.
  • Why would you set an explicit `id` in the manifest rather than letting the browser derive it?
    Identity defaults to being derived from `start_url`. If you later change `start_url` — moving from `/index.html` to `/`, say — the browser can treat the result as a new app, so an update installs alongside the old icon instead of replacing it. A stable `id` decouples identity from the launch URL and makes that change safe.
  • What is the practical difference between an icon with `purpose: "any"` and one with `purpose: "maskable"`?
    `any` icons are drawn as-is. `maskable` icons declare that the artwork keeps its important content inside a central safe zone, so a platform may crop them to a circle, squircle or rounded square. Ship both: a maskable-only icon looks over-padded where no mask is applied, and an `any`-only icon gets its edges clipped by adaptive launchers.

saying these in an interview costs you the question

  • Claiming the manifest is what makes an app work offline
  • Thinking scope can include a different origin
  • Assuming start_url is where the service worker caches from
  • Shipping only a 48px icon and expecting an install prompt
  • Treating short_name as optional decoration rather than the home-screen label

context

open as a page

What does the Background Sync API's `sync` event give you that retrying a failed request inside the page does not, and how do you register one?

level: middleimportance: should knowfreq 34%

basics

~20 s

Background Sync hands a retry to the browser: you call registration.sync.register('tag') and the browser fires a sync event in the service worker once it has connectivity, even after the page is closed. Page-level retries die with the tab.

open as a page

Chromium browsers fire a `beforeinstallprompt` event on `window`. How do you use it to drive your own "Install app" button, and what restrictions apply to calling `prompt()`?

level: middleimportance: should knowfreq 44%

basics

~20 s

Listen for beforeinstallprompt, call preventDefault() to suppress the browser's own UI, and keep the event object. Show your install button, and inside a user gesture call the saved event's prompt(), then await userChoice. The event can be prompted only once.

open as a page

How does a web page subscribe a user to push notifications with the Push API, and what does your application server need from the resulting `PushSubscription` in order to send a message?

level: middleimportance: should knowfreq 40%

basics

~20 s

Call registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey }) on a service worker registration after notification permission is granted. The returned PushSubscription carries an endpoint URL plus p256dh and auth keys; the server needs all three to encrypt and deliver a message.

open as a page

A service that sends web push notifications sees delivery quietly decline over months, with the push service answering 404 and 410 for many stored endpoints. Why do push subscriptions become invalid, and how should the client and server handle it?

level: seniorimportance: should knowfreq 26%

basics

~20 s

Push subscriptions are perishable: revoked permission, cleared site data, browser reinstalls, a rotated VAPID key or push-service expiry all invalidate them. Servers must delete endpoints that return 404 or 410, and clients should re-read getSubscription() on each launch and re-register it.

open as a page

Background Sync is implemented only in Chromium browsers. For a field-service web app whose technicians submit reports from areas with poor connectivity, on Android and iOS alike, how would you design the offline write path?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Build a durable outbox in IndexedDB with server-side idempotency keys, and drive it from a ladder of triggers every browser has: immediate send, the online event, visibility changes, and app launch. Treat Background Sync as an extra trigger on Chromium, never the foundation.

open as a page