Progressive Web App: Service Worker, Offline, Web Push, Add to Home Screen
A PWA isn't a separate technology, it's a set of browser APIs that together make a web application closer to native. You can implement all three components (Service Worker, Web App Manifest, HTTPS) but get a poor PWA. You can do just offline caching and Web Push — and that already delivers tangible value for users.
Service Worker: How It Really Works
Service Worker is a JavaScript proxy between the browser and the network. Runs in a separate thread, no DOM access, lives independently from the page. Main task — intercept network requests and decide where to serve them from: cache, network, or a combination.
Caching strategies for different resources:
Cache First — cache first, then network. For static assets with content hash in the name (scripts, styles, fonts): a file with a hash never changes, can cache forever.
Network First — network first, on error — cache. For API requests that should return current data, but offline it's better to show stale than nothing.
Stale While Revalidate — serve from cache immediately, simultaneously update cache from network. For content where a small delay in freshness is acceptable: article pages, product catalog.
Workbox from Google handles routine logic: cache versioning, cleaning old entries, strategies. Without Workbox, writing a correct Service Worker with proper cache invalidation — 300+ lines of code with non-trivial edge cases.
Vite + vite-plugin-pwa generates Service Worker automatically from config, including precaching all static assets after build.
Offline Mode: What It Really Means
"Works offline" means different things for different products.
Offline reading: news sites, documentation, blogs. Service Worker caches pages on first visit. Stale While Revalidate strategy + Background Sync for syncing when connection recovers.
Offline editing: tasks, notes, forms. IndexedDB stores unsaved data locally. Background Sync API queues the operation — browser syncs itself when connection appears, even if the tab is closed. Limitation: Background Sync only supported in Chromium, not in Safari.
Offline form: user filled out a form offline, pressed submit. Without PWA — error, data lost. With Background Sync — form queued, sent automatically. For medical and insurance forms this is critically important.
Problem often forgotten: sync conflicts. User A edited a record offline, user B changed it online. On sync — conflict. Need a conflict resolution strategy: last-write-wins (risky), three-way merge (complex), or show conflict to user (honest).
Web Push: Notification Delivery
Web Push works through browser + Push Service (separate for each browser: FCM for Chrome/Edge, APNs for Safari). User grants permission → browser subscribes to Push Service → you get endpoint + keys → you send message to Push Service → it delivers to browser.
Implementation: web-push library (Node.js) or equivalent for your backend. VAPID keys generated once. Subscription stored in database — this is your "device token".
Platform limitations in 2024:
- iOS 16.4+ supports Web Push, but only for installed PWA (added to home screen)
- Chrome, Firefox, Edge — full browser support without installation
- Safari macOS — support since macOS Ventura
Notification frequency and relevance directly impact subscriber churn. Users revoke permission if notifications aren't relevant. A/B testing send time and wording — standard practice.
Add to Home Screen and Installation
Web App Manifest — JSON file with application metadata: name, short_name, icons (minimum 192x192 and 512x512 PNG), start_url, display: standalone, theme_color, background_color.
display: standalone hides browser UI — application looks native. display: minimal-ui keeps minimal browser navigation.
Browser shows Install Prompt (beforeinstallprompt) when conditions are met: HTTPS, valid manifest, Service Worker. Prompt can be deferred and shown at the right moment (not immediately on load):
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
});
// Later, on user action:
installButton.addEventListener('click', async () => {
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
});
After installation track via appinstalled event and display-mode: standalone media query.
Performance and PWA
App Shell architecture: minimal HTML/CSS/JS needed to render application shell, cached by Service Worker and loaded instantly. Content loads on top. Difference in perceived speed — noticeable even on slow connections.
Precaching critical resources when installing Service Worker: on first visit cache everything needed for offline work. Precache bundle size affects first install time — don't put everything in there.
Process
Audit current application (Lighthouse PWA score), identify valuable offline scenarios, configure Service Worker through Workbox, implement Web Push if needed, test on real devices. Lighthouse PWA section and Chrome DevTools Application tab — main debugging tools.
Timeline
Basic PWA (manifest + Service Worker + offline static cache): 1–2 weeks on top of a ready app. Web Push integration: 1–2 weeks. Offline editing with IndexedDB + Background Sync: 3–6 weeks depending on data complexity.







