Twitter, Starbucks, and Pinterest all ship PWAs alongside (or instead of) native apps. Why? Because a Progressive Web AppWhat is pwa?A web app enhanced with browser APIs so it can install on a device, work offline, and send push notifications like a native app. gives you offline support, home screen installation, and push notifications without needing the App Store. If you already know how to build a website, you're most of the way there.
What makes a web app "progressive"
The word "progressive" doesn't mean the app is fancy, it means it works for every user, in every context, and then improves when the browser supports more features. Someone on an older phone with a slow connection still gets a working experience. Someone on a modern device with a fast connection gets the full-featured version.
Three qualities define a PWAWhat is pwa?A web app enhanced with browser APIs so it can install on a device, work offline, and send push notifications like a native app.:
Capable
A capable PWA can do things that used to require a native app. It works offline or on a flaky connection, can access device features like the camera and geolocation, and loads quickly because it aggressively caches resources. The browser APIs that make this possible, Cache APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses., IndexedDBWhat is indexeddb?A database built into the browser that stores structured data, supporting large datasets and offline-first apps., Push, Background SyncWhat is background sync?A Service Worker API that queues network requests made while offline and replays them automatically when connectivity is restored., are now well-supported across Chrome, Firefox, Safari, and Edge.
Reliable
Reliability means the app always responds, even when the network doesn't. No white screen, no spinner that spins forever. When a user opens your PWA without internet, a cached version loads instantly. This is one of the biggest wins over a regular website and it's the reason Service Workers exist.
Engaging
An engaging PWA feels like a real app. It installs to the home screen, launches in its own window without a browser address bar, and can send push notifications when the user isn't actively using it. That install moment, when Android or iOS prompts the user to "Add to Home Screen", is what closes the gap between web and native.
Why ship a PWAWhat is pwa?A web app enhanced with browser APIs so it can install on a device, work offline, and send push notifications like a native app. instead of a native app
You don't have to choose one or the other, but here's why teams increasingly prefer PWAs for their primary distribution channelWhat is channel?A typed conduit in Go used to pass values between goroutines - can be unbuffered (synchronous) or buffered (async queue).:
| Factor | Native app | PWA |
|---|---|---|
| Distribution | App Store / Play Store review | Direct URL |
| Updates | User must download update | Instant, automatic |
| Bundle size | 10–100 MB typical | A few hundred KB |
| Platforms | Separate iOS and Android builds | One codebase |
| SEO | None | Full indexing |
| Install friction | Store account required | One tap |
The three files every PWAWhat is pwa?A web app enhanced with browser APIs so it can install on a device, work offline, and send push notifications like a native app. needs
You can turn almost any existing website into a PWA by adding three things:
- A
manifest.json, describes your app to the browser - A
sw.js, your Service WorkerWhat is service worker?A background script the browser runs separately from your web page that intercepts network requests, enabling offline support and caching. script - A registration snippet, tells the browser where to find the Service Worker
That's it. The restWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. is progressive enhancementWhat is progressive enhancement?Building a feature so it works without JavaScript first, then adding richer behavior when JavaScript is available in the browser. on top of a normal web app.
<!-- index.html: the minimum to make a PWA installable -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My PWA</title>
<!-- 1. Link the manifest -->
<link rel="manifest" href="/manifest.json">
<!-- 2. Set the theme color (browser chrome on mobile) -->
<meta name="theme-color" content="#6366f1">
<!-- 3. iOS doesn't read the manifest for the touch icon -->
<link rel="apple-touch-icon" href="/icon-192.png">
</head>
<body>
<div id="app">
<h1>My Progressive Web App</h1>
</div>
<!-- 4. Register the Service Worker -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered, scope:', reg.scope))
.catch(err => console.error('SW registration failed:', err));
}
</script>
</body>
</html>Key technologies at a glance
| Technology | What it does |
|---|---|
| Service Worker | Background script, caching, offline, push |
| Web App Manifest | JSON that defines name, icons, display mode |
| Cache API | Store request/response pairs for offline use |
| IndexedDB | Client-side database for structured data |
| HTTPS | Required for Service Workers to register |
In the next lesson you'll dig into Service Workers, the piece that makes everything else possible.
Quick reference
| File | Purpose |
|---|---|
manifest.json | App identity and install behavior |
sw.js | Service Worker, runs in background |
index.html | Links the manifest, registers the SW |
offline.html | Fallback page shown when network fails |
// Minimal PWA structure
// 1. index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My PWA</title>
<!-- Link to manifest -->
<link rel="manifest" href="/manifest.json">
<!-- Theme for mobile -->
<meta name="theme-color" content="#6366f1">
<!-- Icons -->
<link rel="icon" type="image/png" sizes="192x192" href="/icon-192.png">
<link rel="apple-touch-icon" href="/icon-192.png">
</head>
<body>
<div id="app">
<h1>My Progressive Web App</h1>
<p id="status">Status: <span>Online</span></p>
</div>
<!-- Service Worker registration -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered'))
.catch(err => console.log('SW error', err));
}
</script>
</body>
</html>
// 2. manifest.json
{
"name": "My Super PWA",
"short_name": "PWA",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#6366f1",
"orientation": "portrait",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}