Progressive Web Apps (PWA)
Build PWAs: service worker strategies, Web App Manifest, push notifications, offline-first caching, and Workbox configuration.
Progressive Web Apps provide native-app-like experiences on the web: installability, offline support, push notifications, and fast loading. The core enablers are Service Workers (background scripts) and the Web App Manifest.
Use Workbox for service worker implementation - hand-written service workers have subtle cache/lifecycle bugs. Implement offline-first for content that doesn't change frequently. Ship push notifications only where genuinely useful - don't abuse them.
Workflow
- Add a Web App ManifestThe manifest makes the app installable on Android, iOS, and desktop.
- Implement caching with WorkboxWorkbox handles service worker boilerplate and provides battle-tested caching strategies.
- Handle offline stateUsers on flaky connections need graceful offline experiences - not blank screens.
- Implement Web Push notificationsPush notifications re-engage users even when the app is closed.
- Handle install prompt and iOSCustom install prompts improve conversion; iOS requires manual handling.
Output Format
Web App Manifest with maskable icon, Workbox via vite-plugin-pwa with network strategies, offline fallback page, Background Sync for form queuing, VAPID push, custom install prompt.
Examples
vite-plugin-pwa: `VitePWA({ registerType: "autoUpdate", manifest: { name: "My App", short_name: "App", theme_color: "#6366f1" }, workbox: { globPatterns: ["**/*.{js,css,html,ico,png,svg}"], runtimeCaching: [{ urlPattern: //api//, handler: "NetworkFirst" }] } })`Install prompt: `const [showPrompt, setShowPrompt] = useState(false); useEffect(() => { window.addEventListener("beforeinstallprompt", (e) => { e.preventDefault(); promptRef.current = e; setShowPrompt(true) }) }, []);`Push click: `self.addEventListener("notificationclick", (e) => { e.notification.close(); e.waitUntil(clients.matchAll({ type: "window" }).then((cs) => { const c = cs.find(c => c.url === e.notification.data.url); return c ? c.focus() : clients.openWindow(e.notification.data.url) })) })`Edge Cases & Gotchas
- Safari PWA limitations: iOS Safari doesn't support Web Push (below iOS 16.4), Background Sync, or periodic Background Sync. Test on real iOS devices - DevTools emulation doesn't reflect these gaps
- Service worker update lifecycle: by default, a new SW waits until all tabs close before activating. Add `self.skipWaiting()` + `clients.claim()` in the SW and a "Reload to update" prompt in the UI
- Cache invalidation: Workbox uses content hashing for precached assets. For runtime-cached API responses, configure `expiration: { maxEntries: 50, maxAgeSeconds: 3600 }` to prevent unbounded cache growth
When to Break These Rules
- For B2B dashboards used only on corporate desktops with reliable internet, PWA features (offline, install) add complexity without user benefit. Focus on performance instead.
Reference Files
Workbox docs: https://developer.chrome.com/docs/workboxvite-plugin-pwa: https://vite-pwa-org.netlify.app/web-push npm: https://github.com/web-push-libs/web-pushPWA checklist: https://web.dev/articles/pwa-checklist