Progressive Web Apps (PWA)
A Progressive Web App is a website that behaves like an installed application: it gets an icon on the home screen or desktop, launches in its own window, keeps working offline, and can receive push notifications. Under the hood it's still your web app — the "app-ness" comes from two additions: a web app manifest and a service worker.
The pitch is one codebase reaching every platform with app-like UX and none of the app-store friction. Twitter/X, Starbucks, Pinterest, and Spotify's web player ship as PWAs, and for content, commerce, and productivity apps the pattern routinely replaces "we should build a native app."
TL;DR
- PWA = ordinary web app + manifest (name, icons, display mode → installable) + service worker (programmable network proxy → offline, caching, push).
- The service worker intercepts every request your app makes and decides: serve from cache, hit the network, or combine both.
- Core caching strategies: cache-first (static assets), network-first (API data), stale-while-revalidate (best perceived speed for most content).
- HTTPS is mandatory — service workers don't run on insecure origins.
- Don't hand-roll service workers — use Workbox (or your framework's PWA plugin) for precaching and routing.
- The classic PWA failure is stale-app hell: users stuck on old cached versions because the update lifecycle wasn't handled.
Quick Example
The two files that make a site installable and offline-capable:
Core Concepts
The Service Worker Lifecycle
A service worker is JavaScript that runs separately from your page and proxies its network traffic. Its lifecycle is the part everyone gets wrong:
The "waiting" step exists so two versions never control the same session — but it means deploys don't reach users until they fully close the app. The standard fix is an in-app "Update available — Refresh" toast that calls skipWaiting(), giving users an explicit upgrade path.
Caching Strategies
The service worker chooses per request. The canonical menu:
These mirror the CDN caching vocabulary — the service worker is effectively a CDN edge that lives on the user's device.
Workbox Instead of Hand-Rolling
Raw service workers accumulate subtle bugs (cache versioning, opaque responses, range requests). Google's Workbox packages the patterns:
Framework integrations (vite-plugin-pwa, Next PWA plugins) wire Workbox into your build so the precache manifest updates automatically per deploy.
Installability and App Capabilities
With a valid manifest + service worker over HTTPS, browsers offer installation (Chrome/Edge prompt on desktop and Android; iOS Safari installs via Share → Add to Home Screen). Beyond install, the PWA-adjacent API family covers most "we need native" arguments:
- Push notifications — Web Push (supported on iOS 16.4+ for installed PWAs)
- Background Sync — queue writes made offline, flush when connectivity returns
- File System Access, Web Share, camera/geolocation, badging, shortcuts
The genuinely native-only remainder: deep OS integration (widgets, Siri/Assistant), some Bluetooth/sensor APIs on iOS, and top-tier game performance.
Offline-First Data
Caching the app shell is the easy half; the hard half is data. The standard architecture:
Reads always hit the local store (instant, offline-capable); writes commit locally and queue for sync. Libraries like RxDB, Dexie, Replicache, and PowerSync implement the sync layer — including the truly hard parts (conflicts, ordering). If your app must work offline rather than merely open offline, this is the actual project.
Common Mistakes
No Update Story
Precache everything with cache-first, ship a bug fix, and discover users are pinned to the broken version for weeks. Always: version your caches, clean up in activate, and surface a refresh prompt when a new worker is waiting.
Caching Authenticated API Responses Carelessly
A cache shared at the origin level serving user A's /api/me to user B after account switch — the service-worker flavor of the CDN personalization leak. Keep auth'd endpoints network-only or key caches carefully and purge on logout.
Treating the SW as Optional Infrastructure Nobody Owns
A forgotten service worker is a production incident generator: it silently controls every request for every returning user. It needs an owner, tests, and a kill switch (a trivial self-unregistering worker you can deploy in emergencies).
Precaching the World
Precaching 40 MB of assets on first visit burns mobile data and slows install. Precache the shell; runtime-cache the rest on demand.
Ignoring iOS Differences
iOS supports PWAs but with quirks: install only via Safari's share menu (no prompt), storage can be evicted more aggressively, and push requires the app to be installed. Test the iOS path explicitly.
FAQ
PWA or native app?
Ask what you actually need. Content, commerce, dashboards, productivity → PWA covers it with one codebase and no store review. Deep OS integration, heavy graphics, or iOS-first polish → native or React Native/Flutter. Many teams ship the PWA first and add native only when a concrete capability demands it.
Do PWAs work on iOS?
Yes, with caveats: installation is manual (Share → Add to Home Screen), Web Push arrived in iOS 16.4 for installed PWAs, and some APIs (Web Bluetooth, background sync) are missing. Android/desktop support is fuller.
Does a PWA help SEO or performance?
Indirectly but meaningfully: the caching work behind a PWA typically transforms repeat-visit performance and Core Web Vitals. The manifest itself doesn't affect ranking.
Can I put a PWA in the app stores?
Yes — Trusted Web Activities wrap PWAs for Google Play, and PWABuilder generates store packages for Play and Microsoft Store. iOS App Store requires more wrapping (and Apple's review is less friendly to thin wrappers).
How do I test service workers?
Chrome DevTools → Application tab (inspect registration, caches, force update, simulate offline), Lighthouse's PWA audit for installability, and Playwright for automated offline scenarios. Always test the second deploy — that's where update bugs live.
Related Topics
- Web Performance — What the caching buys you
- CDN — The same strategies, one layer up
- Push Notifications — Web Push in PWAs
- Core Web Vitals — Measuring the gains
- React Native — The cross-platform native alternative
- Vite —
vite-plugin-pwaintegration - Responsive Design — Table stakes for app-like UX