A Progressive Web App is a website with four additions: a manifest, a service worker, an icon set and a few lines of HTML. After that it installs to the home screen, opens without browser chrome, and keeps working when the connection drops. No app store. I test web applications for a living, and the offline path is the test case nobody writes, so this checklist is laid out the way I would lay out the test plan: what to add, then what to prove before it ships.
Two things have changed since most PWA guides were written. Lighthouse dropped its PWA category in version 12 (2024), so 'Lighthouse PWA audit green' is no longer a gate you can run. And iOS has had push notifications for installed web apps since 16.4, but still has no install prompt and a shorter list of APIs than Android. Plan for both.
The manifest is ten fields and three icons
A JSON file that tells the browser what the installed app is called and how it opens:
`json
{
"name": "Your App Name",
"short_name": "App",
"description": "A brief description of your app",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#2563eb",
"orientation": "any",
"icons": [
{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/icons/icon-512-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}
`
Linked from the HTML head:
`html
`
The fields that matter: name is what the install dialog shows, short_name is the home screen label and should stay under 12 characters, start_url is the page that opens on launch (add a query parameter if you want to count launches in analytics), display is standalone for an app feel, fullscreen for games, minimal-ui if you want the back button kept. theme_color colours the status bar; background_color is the splash screen while the app boots.
Icons: 192x192 and 512x512 PNG, plus a 512x512 maskable version with the artwork inside the safe zone, because Android crops icons to its own shape. The Favicon Generator produces the set from one source image.

A service worker: cache first for assets, network first for data
The service worker sits between the page and the network. It is what makes offline, caching and push possible, and it is also where the bugs live.
`javascript
// sw.js
const CACHE_NAME = 'v1';
const ASSETS = [
'/',
'/index.html',
'/styles.css',
'/app.js',
'/icons/icon-192.png'
];
self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME).then(cache => cache.addAll(ASSETS)) ); });
self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then(cached => cached || fetch(event.request)) ); });
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then(keys =>
Promise.all(
keys.filter(key => key !== CACHE_NAME).map(key => caches.delete(key))
)
)
);
});
`
Registered from the main script:
`javascript
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
`
Three strategies cover nearly everything. Cache first (the code above) for static assets that change only on deploy. Network first, falling back to cache, for anything a user would be annoyed to see stale: prices, balances, a list they just edited. Stale while revalidate for content that can be a minute old: serve the cached copy at once, refresh in the background.
The rule I would use: decide the strategy per URL pattern and write it down next to the pattern, because the defect that reaches production is always a data endpoint that was accidentally cache first. In production use Workbox rather than hand-written handlers; it does precaching, expiry and routing with less code and fewer of those mistakes.
The precache list is the install cost. The HTML Minifier and Image Compressor take bytes out of it before they get cached on every device.
The service worker sits between the page and the network.
Offline is a design job and a test job
The service worker is the engine. What the user sees without a connection is design work, and then it is test work.
- An offline page, precached at install, that says the connection is down and lists what still works.
- A stale-content notice when a cached copy is served: 'Showing the version from 14:02. Connect to refresh.' Say the time; users trust a timestamp more than a warning.
- Forms that queue: store the submission in IndexedDB and send it when the connection returns. The Background Sync API does this on Chromium browsers only; on Safari you retry when the page next loads.
- Fallback states for anything that needs the network (search, live data, a profile). An empty state with an explanation, not a spinner that never stops.
Then test it. In Chrome DevTools, Application, Service workers, tick Offline, and walk every path that matters: open the app, move between pages, submit a form, read cached content, come back online and check the queued form actually arrived. Do the same on a phone in flight mode, because the desktop simulation is kinder than a real network dropping mid-request. The offline path is the one that fails in acceptance testing when nobody scripted it, and it fails in a way the user notices.
Install prompt, push permission, and updates
Install. Chrome and Edge fire beforeinstallprompt once the manifest, service worker and HTTPS are in place. Catch it, hold it, and show it from your own button at a moment of your choosing:
`javascript
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); deferredPrompt = e; showInstallButton(); });
function installApp() {
deferredPrompt.prompt();
deferredPrompt.userChoice.then(result => {
if (result.outcome === 'accepted') {
// record the install in your analytics
}
deferredPrompt = null;
});
}
`
Not on the first visit. After three pages, two minutes, or one completed task. A first-visit prompt converts in the low single digits and trains people to dismiss it.
iOS. Safari never fires the event. Users install through Share, then Add to Home Screen. Detect iOS Safari and show a one-line instruction with the share icon; without it, almost nobody finds the option.
Push. Ask after the user has had something from the app, and say what the notifications will contain. Asked cold, around 3% say yes; asked after a task, 15 to 25% is realistic. iOS supports web push only for apps installed to the home screen.
Updates. A new service worker is detected on the next load. Show a 'new version available' bar with a reload button rather than reloading under someone mid-form. That one is worth a test case of its own: deploy, open the old version, confirm the bar appears and that reloading loses nothing.

The launch checklist
Required
- [ ] HTTPS everywhere; service workers do not run without it.
- [ ] Manifest with name, short_name, start_url, display and icons.
- [ ] Icons at 192x192 and 512x512, plus the 512x512 maskable variant.
- [ ] Service worker registered and precaching the shell.
- [ ] Offline page reachable with the network off.
- [ ] Viewport meta tag:
. - [ ]
theme_colorin the manifest and in a meta tag. - [ ] Chrome DevTools, Application, Manifest shows no installability errors. This replaces the Lighthouse PWA audit, which no longer exists.
Recommended
- [ ] Cache first for static assets, network first for API calls, written down per route.
- [ ] Install button shown after engagement, not on arrival.
- [ ] iOS install instructions for Safari users.
- [ ] Tested on a real iPhone and a real Android phone in flight mode.
- [ ] Update bar for new service worker versions.
- [ ] First Contentful Paint under 2 seconds on a mid-range phone.
Optional, Chromium only unless stated
- [ ] Push notifications (also iOS 16.4 and later, installed apps only).
- [ ] Background Sync for queued form submissions.
- [ ] Periodic Background Sync for content refresh.
- [ ] Share Target so the app appears in the system share sheet.
- [ ] Manifest shortcuts and screenshots for a richer install dialog.
If you only prove one thing before launch, prove the offline path on a phone. Everything else fails loudly in the console. That one fails quietly in front of the user.
FAQ
Do PWAs work on iOS?
Yes, with limits. Safari supports service workers, the manifest, home screen install and, since iOS 16.4, web push for installed apps. There is no install prompt, Background Sync is missing, and several Chromium APIs are absent. Test on a real device; Safari behaves differently from Chrome on Android in ways the simulator does not show.
Which browsers can install a PWA?
Chrome, Edge and Samsung Internet on Android and desktop, Safari on iOS and macOS, and Firefox on Android. Desktop Firefox does not offer install; the site still runs, it just stays a tab.
Do I need an app store?
No. If you want a store listing anyway, Google Play accepts PWAs through Trusted Web Activities and the Microsoft Store through PWABuilder. Apple's App Store does not accept a bare PWA wrapper.
How big can the cache be?
Keep the precache under 5 MB; it is downloaded on every install. Chromium lets an origin use a large share of free disk and evicts least-recently-used origins under pressure. Safari is stricter and clears a site's storage after seven days without use unless the app is installed to the home screen. Add expiry to runtime caches so they stay bounded either way.
Can a PWA use the camera, location and sensors?
Camera and microphone, geolocation, accelerometer and gyroscope work everywhere with the user's permission. Web Bluetooth, WebUSB, Web Serial and the File System Access API are Chromium only. Check caniuse.com before promising a feature on iOS.
### Do PWAs work on iOS.
Web Scraping Law and Ethics in 2026: Where the Lines Are
What you may legally scrape in the US and EU in 2026, why robots.txt matters without being law, where GDPR draws the line, and a request rate that avoids blocks.
Dutch Test Data from a Script: The BRP and UPA Generators over API and MCP
Generate BRP persons and UPA pension declarations from your test suite, your seed script or your AI assistant. Every setting on the tool page is a field in the request, the same seed always returns the same data, and a call costs one credit.
Build a Free Brand Identity in One Afternoon
Build a complete brand identity for free: logo, color palette, favicon, and brand assets using free browser tools. No design experience needed.
14 Best Free Online Tools With No Signup (2026)
14 free online tools that work without signup, login, or email in 2026. From QR codes to JSON formatters: instant access, zero friction, full privacy.
