Progressive Web Apps combine the best of websites and native apps. They load like web pages but offer app-like features: they work offline, can be installed on the home screen, send push notifications, and launch in their own window. Companies like Twitter, Pinterest, and Starbucks have seen dramatic improvements in engagement and conversion after switching to PWAs.

What Makes a PWA?

A PWA is any website that meets three criteria:

  • HTTPS: Served over a secure connection (required for service workers)
  • Service Worker: A JavaScript file that runs in the background, handling caching and offline support
  • Web App Manifest: A JSON file that tells the browser how to display the app when installed

Web App Manifest

manifest.json
{
  "name": "My Awesome App",
  "short_name": "MyApp",
  "description": "A progressive web application",
  "start_url": "/",
  "display": "standalone",
  "background_color": "#0f172a",
  "theme_color": "#6366f1",
  "icons": [
    { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },
    { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" },
    { "src": "/icons/icon-512-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
  ]
}
Link in HTML
<link rel="manifest" href="/manifest.json">
<meta name="theme-color" content="#6366f1">

Service Workers

A service worker is a JavaScript file that runs in a separate thread from your main page. It intercepts network requests, manages a cache, and can serve content when the user is offline.

Register a Service Worker
// In your main app.js
if ('serviceWorker' in navigator) {
  window.addEventListener('load', async () => {
    try {
      const reg = await navigator.serviceWorker.register('/sw.js');
      console.log('SW registered:', reg.scope);
    } catch (err) {
      console.error('SW registration failed:', err);
    }
  });
}
sw.js — Cache-First Strategy
const CACHE_NAME = 'v1';
const ASSETS = [
  '/',
  '/css/style.css',
  '/js/app.js',
  '/offline.html',
];

// Install — cache core assets
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(ASSETS))
      .then(() => self.skipWaiting())
  );
});

// Activate — clean 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))
      )
    )
  );
});

// Fetch — serve from cache, fall back to network
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then(cached => cached || fetch(event.request))
      .catch(() => caches.match('/offline.html'))
  );
});

Caching Strategies

  • Cache First: Check cache first, fall back to network. Best for static assets (CSS, JS, images).
  • Network First: Try network first, fall back to cache. Best for API data and HTML pages.
  • Stale While Revalidate: Serve from cache immediately, then update the cache from the network in the background. Best for frequently-updated content.
  • Network Only: Always fetch from network. For non-cacheable requests (POST, authenticated APIs).

Making It Installable

When your PWA meets the install criteria (HTTPS, manifest, service worker), browsers will show an install prompt. You can also trigger it programmatically:

Custom Install Button
let deferredPrompt;

window.addEventListener('beforeinstallprompt', (e) => {
  e.preventDefault();
  deferredPrompt = e;
  document.getElementById('install-btn').style.display = 'block';
});

document.getElementById('install-btn').addEventListener('click', async () => {
  if (!deferredPrompt) return;
  deferredPrompt.prompt();
  const { outcome } = await deferredPrompt.userChoice;
  console.log(`User ${outcome === 'accepted' ? 'installed' : 'dismissed'} the PWA`);
  deferredPrompt = null;
});

Testing Your PWA

  • Chrome DevTools → Application tab — inspect manifest, service worker, cache storage
  • Lighthouse audit — run a PWA audit to check all criteria
  • Offline testing — toggle "Offline" in DevTools Network tab

Try Our Free Performance Tools

Minify your JavaScript and CSS for faster PWA loading.