Service Worker Caching Setup
Service Worker is a script running in background separately from page. Intercepts network requests and manages cache. Foundation for PWA, offline work and instant site re-opening.
Service Worker registration
// src/service-worker-registration.ts
export function registerServiceWorker() {
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js', { scope: '/' })
.then(registration => {
console.log('SW registered:', registration.scope);
// Check for updates every 60 minutes
setInterval(() => registration.update(), 60 * 60 * 1000);
})
.catch(err => console.error('SW registration failed:', err));
});
}
}
Caching strategies
Cache First — for static assets:
// sw.js
const STATIC_CACHE = 'static-v2';
const STATIC_ASSETS = [
'/fonts/inter-regular.woff2',
'/images/logo.svg',
'/offline.html',
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(STATIC_CACHE)
.then(cache => cache.addAll(STATIC_ASSETS))
.then(() => self.skipWaiting())
);
});
self.addEventListener('activate', event => {
// Delete old cache versions
event.waitUntil(
caches.keys().then(keys =>
Promise.all(
keys.filter(key => key !== STATIC_CACHE)
.map(key => caches.delete(key))
)
).then(() => self.clients.claim())
);
});
self.addEventListener('fetch', event => {
if (event.request.destination === 'font' ||
event.request.url.includes('/images/logo')) {
event.respondWith(
caches.match(event.request)
.then(cached => cached || fetch(event.request)
.then(response => {
const clone = response.clone();
caches.open(STATIC_CACHE)
.then(cache => cache.put(event.request, clone));
return response;
})
)
);
}
});
Network First — for HTML pages and API:
const PAGES_CACHE = 'pages-v2';
async function networkFirst(request) {
try {
const response = await fetch(request);
const cache = await caches.open(PAGES_CACHE);
cache.put(request, response.clone());
return response;
} catch {
const cached = await caches.match(request);
return cached || caches.match('/offline.html');
}
}
Stale While Revalidate — for product images:
async function staleWhileRevalidate(request) {
const cache = await caches.open('images-v2');
const cached = await cache.match(request);
const networkPromise = fetch(request).then(response => {
cache.put(request, response.clone());
return response;
});
return cached || networkPromise;
}
Workbox — production-ready solution
// vite-plugin-pwa (for Vite)
import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
runtimeCaching: [
{
// API — network first
urlPattern: /^https:\/\/api\.example\.com/,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
networkTimeoutSeconds: 3,
expiration: {
maxEntries: 50,
maxAgeSeconds: 300,
},
},
},
{
// Images — stale while revalidate
urlPattern: /\.(?:webp|avif|jpg|png|svg)$/,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'images-cache',
expiration: {
maxEntries: 200,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
},
},
},
],
},
}),
],
});
Service Worker update notification
// Show user "Update" button when new SW is ready
navigator.serviceWorker.ready.then(registration => {
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing!;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
showUpdateNotification(() => {
newWorker.postMessage({ type: 'SKIP_WAITING' });
window.location.reload();
});
}
});
});
});
Setup time: 1–2 days for full caching strategy setup via Workbox.







