← Back to all articles
The Architecture DeepDive

PWA Deep Dive

Progressive Web Apps represent a fundamental shift in how we build and distribute applications. This deep dive explores the architecture, trade offs, and real world patterns that make PWAs a compelling alternative to the App Store model.

Author: Barnabas Waweru Reading Time: 18 min Last Updated: August 2026
ansi · wordmark · pwa
██████╗ ██╗    ██╗ █████╗ 
██╔══██╗██║    ██║██╔══██╗
██████╔╝██║ █╗ ██║███████║
██╔═══╝ ██║███╗██║██╔══██║
██║     ╚███╔███╔╝██║  ██║
╚═╝      ╚══╝╚══╝ ╚═╝  ╚═╝
The App Store model has dominated mobile distribution for over a decade. Apple and Google take 15 to 30% of every transaction. App review processes gate what users can access. Native development requires separate codebases for iOS and Android. Progressive Web Apps offer an alternative path: build once, deploy everywhere, bypass the gatekeepers. But the architecture is more nuanced than the marketing suggests. This deep dive examines what PWAs actually are, how they work under the hood, and when they make sense for your architecture.

What Is a Progressive Web App?

The Core Definition

A Progressive Web App is a web application built with modern browser APIs that delivers app like experiences. The key technologies are service workers (for offline caching and push notifications), a web app manifest (which tells the browser how the app should look and behave when installed), and HTTPS (mandatory for service worker registration).

Unlike native apps that run directly on the operating system, PWAs run inside a browser engine. When you "install" a PWA to your home screen, you are creating a shortcut that opens the web app in a standalone browser window without the URL bar.

The Three Pillars

  • Reliable: Loads instantly even in uncertain network conditions. Service workers cache critical assets so the app shell appears immediately.
  • Fast: Responds quickly to user interactions. No server round trip for cached content. Smooth animations and transitions.
  • Engaging: Feels like a native app. Full screen mode, push notifications, home screen icon. Users forget they are in a browser.
PWA Architecture Overview
┌─────────────────────────────────────────────────────────────────┐ │ USER DEVICE │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────┐ ┌──────────────────────────────┐ │ │ │ Home Screen │ │ Browser Engine │ │ │ │ [PWA Icon] │─────▶│ (WebKit/Blink) │ │ │ └──────────────────┘ └──────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ SERVICE WORKER │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ Cache │ │ Fetch │ │ Push │ │ │ │ │ │ API │ │ Intercept │ │ Handler │ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ ┌───────────────────┼───────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐│ │ │ Cache Storage │ │ IndexedDB │ │ Web Storage ││ │ │ (Assets) │ │ (App Data) │ │ (Preferences) ││ │ └─────────────────┘ └─────────────────┘ └─────────────────┘│ │ │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ NETWORK │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Origin CDN │ │ API Server │ │ Push Server │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────────────┘
The Real Innovation

PWAs are not a new technology stack. They are a collection of browser APIs (Service Worker, Cache API, Web App Manifest, Push API) combined into a coherent pattern. The innovation is the pattern, not the individual pieces. Each API existed before the PWA term was coined in 2015.

Core Architecture Components

Web App Manifest

The manifest is a JSON file that tells the browser how to display your app when installed. It defines the app name, icons, theme colors, display mode, and start URL. Without a valid manifest, browsers will not show the install prompt.

// manifest.json { "name": "Architecture DeepDive", "short_name": "DeepDive", "description": "Technical deep dives into system architecture", "start_url": "/", "display": "standalone", "background_color": "#0a0e27", "theme_color": "#00d9ff", "orientation": "portrait", "icons": [ { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" }, { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" } ] }

HTTPS Requirement

Service workers can intercept every network request your app makes. They act as a programmable proxy between your app and the network. This power creates significant security implications. A compromised service worker could inject malicious content into every page. HTTPS is mandatory to prevent man in the middle attacks during service worker registration.

App Shell Architecture

The app shell is the minimal HTML, CSS, and JavaScript required to render your app's UI skeleton. It loads instantly from the cache while dynamic content fetches from the network. This pattern creates the perception of instant loading even on slow connections.

  • Shell: Navigation, header, footer, empty content containers
  • Content: Dynamic data fetched after shell renders
  • Fallback: Offline page shown when network and cache both fail
Installable
Add to home screen with a single tap. Launches in standalone mode without browser chrome. Appears in the app switcher like native apps.
Offline First
Service workers cache critical assets. App loads instantly from cache. Network requests happen in the background.
Push Capable
Send notifications even when the app is closed. Re engage users with timely, relevant updates. Requires explicit user permission.
Linkable
Every state has a URL. Share specific content via links. Deep linking works without special handling.

Service Workers: The Engine Room

What Service Workers Actually Do

A service worker is a JavaScript file that runs in the browser background, separate from your web page. Every request your page makes passes through the service worker, which can intercept it, serve a cached response, fetch from the network, or do both. Service workers also handle push notifications and background sync.

Service workers run on their own thread. They have no DOM access. They communicate with pages via the postMessage API. They can run even when no page is open (for push notification handling).

Service Worker Lifecycle
┌─────────────────────────────────────────────────────────────────┐ │ SERVICE WORKER LIFECYCLE │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Register │───▶│ Download │───▶│ Install │───▶│ Waiting │ │ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ │ │ │ │ │ skipWaiting() │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ ┌──────────┐ ┌──────────┐ │ │ │ │ Activate │◀───│ Activate │ │ │ │ └──────────┘ └──────────┘ │ │ │ │ │ │ │ ▼ │ │ │ ┌─────────────────────────────────────────┐ │ │ │ │ CONTROLLING │ │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ └────────▶│ │ Fetch │ │ Push │ │ Sync │ │ │ │ │ │ Events │ │ Events │ │ Events │ │ │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ └─────────────────────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌──────────┐ │ │ │ Redundant│ (New SW installed) │ │ └──────────┘ │ │ │ └─────────────────────────────────────────────────────────────────┘ Key Events: install → Pre cache critical assets activate → Clean old caches fetch → Intercept network requests push → Handle push notifications sync → Background data synchronization

Registration Pattern

Register your service worker as early as possible in the page lifecycle. Wrap registration in a feature check so the page gracefully degrades in browsers that do not support service workers.

// Register service worker on page load if ('serviceWorker' in navigator) { window.addEventListener('load', async () => { try { const registration = await navigator.serviceWorker.register('/sw.js'); console.log('SW registered:', registration.scope); } catch (error) { console.error('SW registration failed:', error); } }); }

Scope and Control

The folder your service worker sits in determines its scope. A service worker at /app/sw.js can only control requests under /app/. Place your service worker at the root to control all requests. Only one service worker is allowed per scope.

Update Gotcha

A new service worker is installed in the background, but the old one keeps controlling pages until all tabs are closed. Refreshing a page does not activate the new worker. Users must close all tabs using the old worker and navigate back. This catches many developers off guard during deployment.

Caching Strategies

The power of service workers lies in how you handle fetch events. Each request can be routed through different strategies depending on your requirements for freshness, speed, and offline capability.

01
Cache First
Check cache, return if found. Only hit network on cache miss. Best for static assets that rarely change.
02
Network First
Try network, fall back to cache on failure. Best for dynamic content that must be fresh when possible.
03
Stale While Revalidate
Return cached version immediately, fetch update in background. Best balance of speed and freshness.
04
Network Only
Always fetch from network, never cache. Best for non GET requests and highly sensitive data.
// Stale While Revalidate Strategy self.addEventListener('fetch', (event) => { event.respondWith( caches.open('dynamic-v1').then(async (cache) => { const cachedResponse = await cache.match(event.request); // Fetch fresh version in background const fetchPromise = fetch(event.request).then((networkResponse) => { cache.put(event.request, networkResponse.clone()); return networkResponse; }); // Return cached immediately, or wait for network return cachedResponse || fetchPromise; }) ); });

Workbox: The Standard Tooling

Workbox is Google's library for service worker development. It provides pre built caching strategies, precaching support, and routing utilities. In 2026, Workbox remains the standard toolkit for service worker strategies and integrates cleanly with Vite, webpack, and Next.js (and similar) build pipelines via plugins and injectManifest patterns. The Chrome DevTools Application panel provides complete debugging for service workers, cache storage, and manifest inspection.

Cache Versioning

Always version your cache names (e.g., static-v2). In the activate event, delete old caches to prevent storage bloat. Safari imposes a 50MB limit on cache storage for PWAs. iOS can automatically clear PWA storage if the app is not used for a few weeks.

PWA vs Native: The Real Comparison

Capability PWA Native iOS Native Android
Development Cost Single codebase Separate Swift/SwiftUI Separate Kotlin
App Store Distribution Not directly Full access Full access + PWA
Push Notifications iOS 16.4+, limited Full support Full support
Offline Capability Service workers Full control Full control
Hardware Access Camera, GPS, limited Full access Full access
Background Processing Very limited Background tasks Background services
Performance Good, not native Optimal Optimal
Updates Instant, no review App Store review Play Store review
SEO / Discoverability Fully indexable ASO only ASO only
Payment Commissions 0% (your processor) 15 to 30% 15 to 30%
The Distribution Advantage

PWAs bypass app stores entirely, offering independence and easier distribution. No 15 to 30% commission. No review delays. No geographic restrictions. Users can access your app instantly via URL. But you lose App Store discoverability and the trust signal of being in a curated marketplace.

The iOS Reality Check

Apple's Cautious Approach

Apple has been historically reluctant to fully support PWAs. All iOS browsers must use WebKit (Safari's engine), giving Apple complete control over web capabilities on iOS. This limits competition and innovation in web standards on iOS. PWA features depend entirely on Apple's implementation priorities.

What Works (iOS 16.4+)

  • Add to Home Screen with app icon
  • Standalone display mode (no browser chrome)
  • Push notifications (when installed to home screen)
  • Service worker caching
  • Web App Manifest basic features
  • Badging API
Critical iOS Limitations

No App Store presence: PWAs cannot be listed in the Apple App Store, cutting them off from the primary discovery channel US users rely on.

Storage limits: Safari historically imposed tight cache quotas (often cited around 50MB for some PWA contexts); treat storage as ephemeral and design for eviction. iOS may clear PWA storage if the app is unused for weeks.

Install UX (2026): There is still no beforeinstallprompt on iOS. Users add via Share → Add to Home Screen. From iOS 16.4+, install is available from the Share menu in Safari and other browsers. Guide users with explicit UI copy—do not rely on Chrome-style install banners.

No background sync: Background processing is heavily limited to save battery.

No install prompt: Users must manually use Share > Add to Home Screen. No automatic install banner.

Push notification friction: Permission must be requested from within the installed PWA. Service worker listeners may not trigger reliably after device restarts.

EU Regulatory Pressure

In April 2025, the EU fined Apple 500 million euros for DMA non compliance related to browser engine restrictions. The UK CMA followed in October 2025 by designating Apple with Strategic Market Status. However, as of early 2026, these rulings have not materially changed PWA capabilities on iOS. Apple's BrowserEngineKit implementation creates enough friction that zero browsers have adopted alternative engines.

The Platform Gap

Android treats PWAs as first class citizens with broad API support. iOS treats them as a limited subset of what the web can do. If your user base is iOS heavy, this gap is a critical architectural decision point. The gap is closing, but slowly.

The Business Case: Real Numbers

2x
Daily Active Users
Starbucks PWA
65%
Increase Pages/Session
Twitter Lite
76%
More Conversions
Alibaba PWA
70%
Reduced Data Usage
Twitter Lite

Success Stories

Starbucks: Built their PWA to let customers browse the menu and customize orders without reliable internet. The offline first design reached customers in areas with spotty connectivity. The PWA is 0.4% the size of the native app. Doubled daily active users among web visitors.

Twitter Lite: Reduced storage requirement to less than 5% of the native Android app. 75% increase in tweets sent. 20% decrease in bounce rates. Critical for emerging markets with data caps and older devices.

Pinterest: Time spent on platform increased 40%. User generated ad revenue jumped 44%. Core engagement up 60%.

Trivago: 150% increase in engagement. 97% increase in conversions. Users returned at least twice in 14 days after installation.

Cost Analysis

Development: PWAs cost 40 to 60% less than maintaining separate iOS and Android apps. Single codebase, single team, single deployment pipeline.

Distribution: No $99/year Apple Developer Program fee. No 15 to 30% commission on transactions. No app review delays.

Maintenance: Updates deploy instantly. No waiting for store approval. Fix bugs in production immediately.

The Real Question

PWAs are not about whether they are better than native. They are about whether they are good enough for your use case. Content apps, e commerce, news, productivity tools: PWAs often exceed requirements. Games, AR/VR, heavy hardware integration: native still wins.

Implementation Patterns

The Minimum Viable PWA

To pass browser PWA audits and enable installation, you need three things:

  • Valid web app manifest with name, icons, start URL, display mode
  • Registered service worker (even if it does nothing)
  • HTTPS on all pages
// Minimal service worker (sw.js) const CACHE_NAME = 'app-shell-v1'; const SHELL_ASSETS = [ '/', '/index.html', '/styles.css', '/app.js', '/icons/icon-192.png' ]; // Pre cache shell on install self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(SHELL_ASSETS)) .then(() => self.skipWaiting()) ); }); // Clean old caches on activate self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then(keys => Promise.all( keys.filter(key => key !== CACHE_NAME) .map(key => caches.delete(key)) ) ).then(() => self.clients.claim()) ); }); // Cache first for shell, network first for API self.addEventListener('fetch', (event) => { const url = new URL(event.request.url); if (url.pathname.startsWith('/api/')) { // Network first for API calls event.respondWith( fetch(event.request) .catch(() => caches.match('/offline.html')) ); } else { // Cache first for static assets event.respondWith( caches.match(event.request) .then(response => response || fetch(event.request)) ); } });

Framework Integration

Next.js: Use next-pwa package or the built in PWA guide. Server side rendering ensures search engines index all content.

Vite: vite-plugin-pwa provides zero config PWA generation with Workbox integration.

Create React App: Service worker template included by default, though CRA is being deprecated in favor of frameworks.

Testing Your PWA

  • Lighthouse: Chrome DevTools > Lighthouse > PWA audit. Gives pass/fail checklist for installability, offline behavior, best practices.
  • Application Panel: Chrome DevTools > Application. Inspect manifest, service worker state, cache storage, IndexedDB.
  • Offline Testing: DevTools > Network > Offline checkbox. Verify your app shell loads without network.
  • Mobile Testing: Remote debug on real devices. Emulators miss edge cases in service worker behavior.
Common Mistakes

Caching too much: Do not cache everything. Cache the shell and critical assets. Let dynamic content use network first strategies.

Ignoring updates: Users can run old versions indefinitely. Implement update notification UI. Consider skipWaiting() for critical fixes.

Assuming iOS parity: Test on real iOS devices. Safari has different storage limits, eviction policies, and notification behavior.

Key Takeaways

1. PWAs Are Architecture, Not Magic

A PWA is not a checkbox you enable. It is an architectural pattern combining service workers, caching strategies, and web app manifests into a coherent offline first experience. The quality depends entirely on your implementation.

2. Service Workers Are the Core

Everything flows through the service worker. It is a programmable network proxy that determines how your app handles requests, stores assets, and responds to push events. Master service worker lifecycle and caching strategies.

3. iOS Is the Limiting Factor

Android treats PWAs as first class citizens. iOS treats them as second class web apps with significant limitations. If your audience is iOS heavy, factor this into your architecture decision. The gap is narrowing but still substantial.

4. Distribution Is the Real Win

No App Store commissions. No review delays. Instant updates. SEO indexable. Shareable via URL. These distribution advantages often outweigh feature limitations for content apps, e commerce, and productivity tools.

5. Choose Based on Requirements

PWAs excel for content first, reach focused, budget conscious projects. Native apps excel for performance critical, hardware dependent, store monetized products. Many companies run both: PWA for reach, native for power users.

6. Test on Real Devices

Service worker behavior differs across browsers and platforms. iOS Safari has quirks that emulators miss. Storage limits, notification reliability, and offline behavior need real device testing.

7. The Spec Has Stabilized

Chrome, Edge, Firefox, and Safari all support service workers and Web App Manifest without flags. The era of browser fragmentation as a PWA blocker is largely over. Tooling has matured. The question is no longer "can we build a PWA" but "should we."

8. This Is Production Technology

Starbucks, Twitter, Pinterest, Alibaba, Spotify, Telegram, and thousands of other companies run PWAs in production. The patterns are proven. The business impact is documented. PWAs are not experimental technology.

Decision Framework: When to Choose PWA

Choose PWA When

  • Your app is content heavy (media, news, publishing, e learning)
  • SEO and organic traffic are critical success factors
  • Budget constraints require single codebase development
  • Rapid iteration and instant updates are priorities
  • You want to avoid App Store commissions
  • Offline browse/read capability is sufficient
  • Your audience includes emerging markets with data caps

Choose Native When

  • Performance is critical (games, video editing, AR/VR)
  • Deep hardware integration required (sensors, Bluetooth, NFC)
  • App Store presence is mandatory for discoverability
  • Complex background processing needed
  • Subscription revenue through App Store is the model
  • Your iOS user base expects native experience

Consider Hybrid When

  • PWA for web discoverability and casual users
  • Native apps for power users who want full features
  • Shared web views wrapped in native shells
  • Gradual migration from web to native as you prove market fit
// Share this deep dive

Send with a live card — iMessage, SMS, X, Facebook, WhatsApp, Instagram, TikTok.