Progressive Web Apps (PWAs) combine the best of web and native applications. They are regular websites that use modern browser APIs to deliver app-like experiences — installable, fast, reliable, and capable of working offline.
What Makes a PWA?
A PWA is not a specific technology. It is a set of patterns and APIs that, together, make a web app feel like a native app.
Core Characteristics
| Feature | Description |
|---|---|
| Installable | Users can add it to their home screen |
| Offline-capable | Works without an internet connection |
| Responsive | Fits any screen size |
| Linkable | Shared via URL, no app store needed |
| Secure | Served over HTTPS |
| Re-engageable | Push notifications bring users back |
| Fresh | Always up-to-date via service worker updates |
PWA vs Native Apps vs Traditional Web
| Capability | Traditional Web | PWA | Native App |
|---|---|---|---|
| Works offline | No | Yes | Yes |
| Installable | No | Yes | Yes |
| Push notifications | No | Yes | Yes |
| App store required | No | No | Yes |
| URL-based sharing | Yes | Yes | No |
| Automatic updates | Yes | Yes | No |
| Device API access | Limited | Good | Full |
| Cross-platform | Yes | Yes | No |
The Three Pillars
Every PWA is built on three core technologies:
1. HTTPS
PWAs require a secure connection. Service workers — the technology that enables offline support — only work over HTTPS (or localhost during development).
2. Web App Manifest
A JSON file that tells the browser about your app — its name, icons, colors, and how it should behave when installed.
{
"name": "Sabaoon Academy",
"short_name": "Academy",
"description": "Learn web development with practical courses",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#E21B1B",
"icons": [
{
"src": "/icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}Link it in your HTML:
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#E21B1B" />Display Modes
| Mode | Description |
|---|---|
fullscreen | No browser UI at all |
standalone | Looks like a native app (no address bar) |
minimal-ui | Small set of browser controls |
browser | Normal browser tab |
3. Service Worker
A JavaScript file that runs in the background, separate from the web page. It intercepts network requests, caches resources, and enables offline functionality.
// Register a service worker
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);
}
});
}The Service Worker Lifecycle
Service workers go through a specific lifecycle:
1. Registration
The page tells the browser where the service worker file lives.
2. Installation
The browser downloads and parses the service worker. The install event fires, and this is where you typically cache your app shell.
// sw.js
const CACHE_NAME = "app-v1";
const ASSETS = ["/", "/index.html", "/styles.css", "/app.js"];
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS))
);
});3. Activation
After installation, the service worker activates. This is where you clean up old caches.
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys
.filter((key) => key !== CACHE_NAME)
.map((key) => caches.delete(key))
)
)
);
});4. Fetch Interception
The activated service worker intercepts all network requests from pages within its scope.
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request).then((cached) => {
return cached || fetch(event.request);
})
);
});PWA Installability
For a PWA to be installable, it needs:
- A valid web app manifest with required fields
- A registered service worker with a
fetchevent handler - To be served over HTTPS
- An icon at least 192x192 pixels
The Install Prompt
Browsers show an install prompt when these criteria are met. You can also create a custom install button:
let deferredPrompt;
window.addEventListener("beforeinstallprompt", (event) => {
event.preventDefault();
deferredPrompt = event;
showInstallButton();
});
async function handleInstallClick() {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
console.log(`User ${outcome === "accepted" ? "installed" : "dismissed"} the app`);
deferredPrompt = null;
}Auditing with Lighthouse
Google's Lighthouse tool audits your PWA against best practices:
# Run from Chrome DevTools → Lighthouse tab
# Or via CLI:
npx lighthouse https://your-site.com --view
Lighthouse checks:
- Service worker registration
- HTTPS usage
- Web app manifest validity
- Offline capability
- Performance metrics
- Accessibility
App Shell Architecture
The app shell is the minimal HTML, CSS, and JavaScript needed to render the UI. Cache it during install so the app loads instantly, even offline.
App Shell (cached):
├── index.html (layout, navigation)
├── styles.css (all styles)
├── app.js (core JavaScript)
└── icons/ (app icons)
Dynamic Content (fetched from network):
├── /api/posts (blog posts)
├── /api/courses (course data)
└── /api/user (user profile)The shell loads from cache immediately, then dynamic content loads from the network (or from cache if offline).
Practical Exercise
Create a minimal PWA setup:
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scal=1.0" />
<meta name="theme-color" content="#E21B1B" />
<link rel="manifest" href="/manifest.json" />
<title>My PWA</title>
</head>
<body>
<h1>My Progressive Web App</h1>
<p id="status">Checking connection...</p>
<script>
// Register service worker
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}
// Update connection status
function updateStatus() {
const el = document.getElementById("status");
el.textContent = navigator.onLine ? "Online" : "Offline";
el.style.color = navigator.onLine ? "green" : "red";
}
window.addEventListener("online", updateStatus);
window.addEventListener("offline", updateStatus);
updateStatus();
</script>
</body>
</html>Key Takeaways
- PWAs are web apps that use HTTPS, a manifest, and a service worker to deliver native-like experiences.
- The web app manifest controls how the app appears when installed (name, icons, display mode).
- Service workers run in the background and enable offline support by intercepting network requests.
- The service worker lifecycle follows: register, install, activate, and fetch.
- The app shell architecture caches the UI skeleton for instant loads, then fetches dynamic content separately.