// blog/developer/
Back to Blog
Developer · August 3, 2026 · 9 min read · Updated May 22, 2026

Progressive Web App Checklist 2026: What to Ship and Skip

Progressive Web App Checklist 2026: What to Ship and Skip

A Progressive Web App is a website that behaves like a native app. Users install it on the home screen, run it offline, receive push notifications, and use it full-screen without browser chrome. All without an app store submission.

The tech has matured. In 2026 PWAs run on Chrome, Edge, Safari, Firefox, and Samsung Internet. Apple has expanded iOS PWA capabilities (though still narrower than Android). The gap with native shrinks every year.

Turning an existing site into a PWA is not a rewrite. It is four additions: a manifest file, a service worker, an icon set, and a few configuration lines. This checklist walks through them.

* * *

Web App Manifest

A JSON file that tells the browser how the app behaves when installed:

`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" } ] } `

Link from HTML:

`html `

Fields worth tuning:

  • name: full name shown during install.
  • short_name: home-screen label, keep under 12 characters.
  • start_url: the page that opens on launch.
  • display: standalone for app-like, fullscreen for games, minimal-ui to keep browser controls.
  • theme_color: colors the address bar and status bar.
  • background_color: splash background while the app boots.

Generate the full icon set with the Favicon Generator. Required minimum: 192×192 and 512×512 PNG plus a 512×512 maskable variant for Android.

PWA install prompt on mobile phone screen
PWA install prompt on mobile phone screen
* * *

Service Worker Essentials

The service worker runs in the background and enables offline, caching, and push:

`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)) ) ) ); }); `

Register from your main script:

`javascript if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js'); } `

Three caching strategies cover most cases:

  • Cache first (above): serve from cache, fall back to network. Best for static assets.
  • Network first: try network, fall back to cache. Best for dynamic content.
  • Stale while revalidate: serve from cache immediately, refresh the cache in the background. Best balance of speed and freshness.

For production, use Workbox (from Google). It handles caching, expiration, and precaching with much less custom code.

Minify HTML before precaching with the HTML Minifier so the precache footprint is as small as possible.

Key takeaway

The service worker runs in the background and enables offline, caching, and push: ```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)) ) ) ); }); ``` Register from your main script: ```javascript if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js'); } ``` Three caching strategies cover most cases: - **Cache first** (above): serve from cache, fall back to network.

* * *

Designing the Offline Experience

A service worker is the engine. The offline UX is design work.

  • Offline fallback page: a dedicated page that explains the connection is down and lists what is still available. Precache it on install.
  • Cached content indicator: when serving stale content, say so. A subtle banner like "Viewing cached version. Connect to refresh." sets expectations.
  • Forms while offline: store submissions in IndexedDB and sync when the connection returns via the Background Sync API.
  • Graceful degradation: features that need the network (search, profiles, live data) should render meaningful fallback states, not blank screens or generic errors.
  • Test offline: in Chrome DevTools, Application > Service Workers > Offline. Walk every critical path: navigation, form submit, content viewing.

Before precaching images, run them through the Image Compressor. A smaller cache footprint means faster installs and less device storage used.

* * *

Install, Notifications, and Updates

Install prompt: Chrome fires beforeinstallprompt when your PWA meets the criteria (manifest, service worker, HTTPS). Defer it and trigger from your own button:

`javascript let deferredPrompt;

window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); deferredPrompt = e; showInstallButton(); });

function installApp() { deferredPrompt.prompt(); deferredPrompt.userChoice.then(result => { if (result.outcome === 'accepted') console.log('App installed'); deferredPrompt = null; }); } `

Timing: never on first visit. Wait until the user has visited 3+ pages, spent 2+ minutes, or completed a meaningful action. First-visit prompts convert at single digits.

iOS: Safari does not fire beforeinstallprompt. Users install via Share > Add to Home Screen. Detect iOS Safari and show custom instructions with an arrow.

Push notifications: ask after the user has gotten value, not before. Explain what the notifications will contain. First-visit permission requests run around 3% acceptance. After engagement, 15-25% is realistic.

App updates: a new service worker detects automatically. Show a "New version available" banner with a refresh button instead of force-refreshing mid-task.

Web app running in standalone mode on tablet
Web app running in standalone mode on tablet
* * *

The Launch Checklist

Before you ship:

Required

  • [ ] HTTPS (PWAs require secure context).
  • [ ] Manifest with name, icons, start_url, display.
  • [ ] Icons: 192×192, 512×512, plus maskable 512×512 for Android.
  • [ ] Service worker registered and caching critical assets.
  • [ ] Offline fallback page works disconnected.
  • [ ] Viewport meta tag: .
  • [ ] theme_color in both manifest and meta tag.

Recommended

  • [ ] Cache-first for static assets, network-first for API calls.
  • [ ] Custom install prompt triggered after engagement.
  • [ ] Splash screen (background_color + icon in manifest).
  • [ ] Responsive design across phone, tablet, desktop.
  • [ ] Lighthouse PWA audit green.
  • [ ] First Contentful Paint under 2 seconds.

Advanced

  • [ ] Push notifications with permission request after engagement.
  • [ ] Background sync for offline form submissions.
  • [ ] Periodic background sync for content updates.
  • [ ] Share Target API for receiving shared content.
  • [ ] Manifest shortcuts for quick actions.
  • [ ] Manifest screenshots for richer install UI.

Run Lighthouse (Chrome DevTools > Lighthouse > Progressive Web App) to verify everything before launch.

* * *

FAQ

Do PWAs work on iOS?

Yes, with limits. Safari supports service workers, the web app manifest, and home screen install. Push notifications arrived in iOS 16.4 (2023), background sync is limited, and some Android-available Web APIs are still missing. Test on iOS, the behavior diverges from Chrome on Android.

Do I need to submit a PWA to an app store?

No. PWAs install from the browser. You can optionally submit to Google Play (via Trusted Web Activities), the Microsoft Store, and the Samsung Galaxy Store for extra distribution. Apple's App Store does not accept PWAs.

How big should the cache be?

Keep the initial precache (assets for offline) under 5 MB. Total cache can grow with use; add expiration to keep it bounded. Most browsers allow 50-100 MB per origin.

Can a PWA access device features?

Yes. MediaDevices (camera), Geolocation, accelerometer, gyroscope, and Web Bluetooth all work. File System Access, WebUSB, and Web Serial are Chromium-only. Each API needs explicit user permission. Check caniuse.com for browser-specific support.

Key takeaway

### Do PWAs work on iOS.