Web App Manifest, Installability, Push/Background Sync & Offline UX
The manifest names the installed app. Installability is HTTPS, a worker, icons, and engagement. App shell, push, and background sync still have platform gaps, especially on iOS.
- 1Gist
- 2Maps
- 3Q&A
- 4Sandbox
Voice readout needs Web Speech Synthesis in this browser.
Question ladder
L1
Which manifest fields show up in an install interview?
Answer
name, short_name, icons including a maskable icon, start_url, display, scope, id, and the theme and background colors.
L2
What are the classic installability ingredients?
Answer
HTTPS, a valid manifest, and a controlling Service Worker with a fetch handler, plus a browser-specific engagement rule.
L3
Why split the app shell from network data?
Answer
The shell paints chrome from cache. Lists and mutations keep their own freshness and auth rules.
L4
What does changing id or start_url do?
Answer
A new id can look like a second installed app. A bad start_url opens the wrong screen after launch.
L5
How is web push different from a local notification?
Answer
Web push is server-driven through a push service into the Service Worker. A local notification is not a substitute for a server alert.
L6
What is background sync for, and what replaces it?
Answer
It retries a tagged queue when connectivity returns. Where sync is missing, flush on the online event while a tab or worker is awake.
L7
What do you say about iOS in an interview?
Answer
Add to Home Screen and Service Worker support exist with limits. Push and background sync have lagged. Check the current Safari notes. Do not claim parity with Chromium or with native APNs.
Failure modes
Manifest not linked
The file exists and the browser never sees it because the page omitted the link rel=manifest tag.
display browser
The installed window still looks like a tab, so the install feels like a bookmark.
Scope narrower than the product
In-app links jump out to browser chrome.
Silent offline
Failed fetches look like an empty product. There is no badge and no queued write.
Push treated as native APNs
Web push uses VAPID and the Push API. Native delivery is a different stack, and iOS web push is version-dependent.
Background sync assumed everywhere
The tag never fires. Drafts sit in IndexedDB until an online listener runs.
Misconceptions
A manifest alone makes a PWA.
Installability also wants a controlling Service Worker and a secure origin.
beforeinstallprompt exists in every browser.
It is a Chromium event. Safari uses a different add-to-home-screen flow.
Web push and FCM-to-iOS are the same path.
Web push ends in a Service Worker. Native push uses APNs or FCM and does not share that lifetime.
Interviewer traps
Reciting every manifest key.
Name the fields that change install UI, identity, and scope, then move to offline UX.
Promising background sync on iOS.
Say you would check current support and ship an online-event fallback.
The dashboard must open from the home screen
Prefer
Manifest plus a controlling worker
The OS gets a name, an icon, and a start URL. The worker paints a shell when the network is gone.
- display standalone so it does not look like a leftover tab.
- scope and id stay stable so you do not mint a second app.
- Offline is a badge and a queue, not an empty list.
Alternative
A manifest file with no worker
Some browsers show an icon and then load the network or nothing.
- Install criteria fail where a fetch handler is required.
- There is no pantry for the shell.
- Push and background sync have nowhere to land.
Launch, shell, then data
- 1
Open start_url
The manifest picked this door, including any source parameter you use for analytics. - 2
Paint the cached shell
Chrome of the app comes from Cache Storage, not from a fresh document wait. - 3
Boot the script
The page can register sync and listen for a waiting worker. - 4
Fetch data or show the badge
Online uses network-first or stale-while-revalidate. Offline shows the last lists. - 5
Queue writes
Background sync when it exists. An online listener when it does not.
Overview
The Web App Manifest names your app for install UI. Installability is a checklist: HTTPS, a Service Worker, a manifest, and engagement heuristics. Pair that with an app shell, optional push, and background sync. Knowing platform gaps, especially iOS, beats memorizing every JSON key.
Link the file from the document:
<link rel="manifest" href="/manifest.webmanifest" />Fields that matter
{
"name": "Ops Desk",
"short_name": "OpsDesk",
"start_url": "/?source=pwa",
"display": "standalone",
"background_color": "#0b1020",
"theme_color": "#3b82f6",
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
],
"id": "/",
"scope": "/"
}| Field | Why it matters | Pitfall |
|---|---|---|
name / short_name | Install dialog and home-screen label | Too long, so the OS truncates it |
start_url | Where launch opens | Wrong deep path, or analytics source forgotten |
display | standalone, minimal-ui, fullscreen, or browser | browser still feels like a tab |
icons | Install and splash | Missing 192 and 512, or no maskable safe zone |
scope | Which URLs stay "in app" | Narrow scope jumps out to browser chrome |
id | Identity across updates | Changing it can look like a new app |
theme_color / background_color | Browser chrome and splash | Poor contrast |
Installability
| Requirement | Chromium-class | Notes |
|---|---|---|
| HTTPS or localhost | Required | Mixed content breaks trust |
| Manifest with required fields | Required | name, icons, start_url, display |
| Service Worker controlling the page | Required for the classic criteria | A fetch handler is part of that story |
| Icons | Required sizes | Prefer a maskable icon |
| User engagement | Often required | Idle install-prompt policies vary |
beforeinstallprompt | Chromium | Capture it for a custom button. It is not universal |
Safari / iOS: treat it as a partial PWA platform. Add to Home Screen is a different gesture. Service Workers exist with limits. Push has lagged by release. In an interview, say "I would check the current Safari notes" and do not invent this quarter's matrix. Web push is VAPID plus the Push API. Native APNs, FCM, and a notification service extension are a different delivery plane. Do not assume parity.
App shell
Flow
- 1
1. Launch start_url
- next2. Paint the cached app shell
- 2
2. Paint the cached app shell
- next3. Boot the app script
- 3
3. Boot the app script
- next4. Online uses network-first or SWR
- 4
4. Online uses network-first or SWR
- next5. Offline shows cached lists and a badge
- 5
5. Offline shows cached lists and a badge
- next6. Queue writes for background sync
- 6
6. Queue writes for background sync
Lesson map
Web App Manifest, Installability, Push/Background Sync & Offline UX
The manifest names the installed app. Installability is HTTPS, a worker, icons, and engagement. App shell, push, and background sync still have platform gaps, especially on iOS.
Architecture. Architecture
Select a node to see why it exists, or an edge to see the protocol, direction, effect, and consequence.
Mermaid export
flowchart TB a["1. Launch start_url"] b["2. Paint the cached app shell"] c["3. Boot the app script"] d["4. Online uses network-first or SWR"] a -->|1. Launch start_url| b b -->|2. Paint the cached app shell| c c -->|3. Boot the app script| d
The shell is the frame: HTML, CSS, and the boot script. It is allowed to be cache-first because you version it. Lists and drafts are not the shell.
- The shell paints in under a second from cache.
- The offline indicator is explicit. A failed fetch is not an empty product.
- Mutations queue with visible "will sync" copy.
- You have a conflict policy when sync returns (last-write-wins, or a server 409 the UI can show).
- A waiting worker raises an update toast. There is no silent half-upgrade.
Push, at the platform layer
Web push is permission, a pushManager subscription with a VAPID key, your server storing the subscription, and a push handler in the worker that calls showNotification. notificationclick focuses or opens a window. userVisibleOnly: true means you cannot push a silent background job and call it a notification.
FCM can fan out to web and mobile. That does not make iOS web push the same code path as a native app. Say the two planes out loud.
self.addEventListener("push", (event) => {
const data = event.data ? event.data.json() : { title: "Ping", body: "New event" };
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
data: { url: data.url || "/" },
})
);
});
self.addEventListener("notificationclick", (event) => {
event.notification.close();
event.waitUntil(self.clients.openWindow(event.notification.data.url || "/"));
});Background sync
The page writes the draft to IndexedDB, then registration.sync.register("flush-drafts") when sync exists. The worker's sync event runs flushDrafts for that tag. Tags coalesce. Support is uneven. Periodic background sync is more restricted and engagement-gated.
Where sync is missing, listen for online and flush while the page is open. That fallback does not run if nobody has a window.
| Mechanism | Strength | Weakness |
|---|---|---|
| Background Sync | Retries when connectivity returns | Support gaps. Tags coalesce |
| Periodic Background Sync | Can refresh a cache on an interval | Restricted and engagement-gated |
online event | Available wherever the page runs | Needs an open tab or an awake worker |
Install and flush, in memory
Press Run. Snippets must be self-contained — no network, files, or native modules.
Interview Q&A
What are the classic installability ingredients?
Answer
HTTPS, a valid manifest (icons, name, start_url, display), and a controlling Service Worker, plus whatever engagement rule that browser applies.
Why an app shell plus network data?
Answer
The shell gives instant chrome offline. Data strategies stay correct for freshness and auth. Mixing them is how you cache a personalized document.
How is push different from a local notification?
Answer
Push is server-driven through a push service into the Service Worker. A locally scheduled notification is not a substitute for a server-initiated alert.
What does scope do after install?
Answer
URLs inside scope feel like the app. URLs outside it can open in a normal browser tab. Set scope to the product, not to one nested path, unless you mean that.
Why include both any and maskable icons?
Answer
any is the legacy icon. maskable leaves a safe zone so the OS can crop to a circle or a squircle without cutting the mark.
Where does beforeinstallprompt not exist?
Answer
It is not a Safari API. Capture it on Chromium for a custom button, and keep a small note for the manual add-to-home-screen path.
A draft was saved on a train. What runs later?
Answer
If background sync registered, the worker flushes when the browser decides connectivity is back. If not, the queued IndexedDB row waits for an online event in an open tab.
What do you tell an interviewer about iOS?
Answer
Partial platform. Confirm the current Safari version for install, Service Worker limits, and web push. Do not quote native APNs behavior as if it were the Push API.
Pitfalls
start_urlpointing at a logged-out deep link.- Changing
idduring a redesign and orphaning installed clients. - Showing a custom install button that no-ops when the event never fired.
- Calling web push "the same as our iOS app" in a design review.
The file has display browser, one 64px icon, start_url /admin/drafts/9, and no id. The page is HTTP. Say which install checks fail, and what the home screen would do if a browser installed it anyway.