Service Worker Registration
Register a Service Worker from HTML, understand its lifecycle, and use it to enable offline capability and background sync.
Overview
Key Points
- β’Register with `navigator.serviceWorker.register('/sw.js', { scope: '/' })`
- β’The registration is a Promise; catch errors for HTTPS/file-system issues
- β’SW lifecycle: installing β installed β activating β activated (waiting: blocks until all clients close)
- β’`skipWaiting()` + `clients.claim()` in the SW immediately activates a new version
- β’The `fetch` event in sw.js intercepts every network request from the SW's scope
- β’Cache API: `caches.open(name)`, `cache.put(req, res)`, `cache.match(req)` for storing responses
- β’SW does NOT have access to the DOM communicate via `postMessage` / `BroadcastChannel`
Examples
Registering a service worker from the HTML page
<!-- In your main HTML file's <script> -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
try {
const registration = await navigator.serviceWorker.register('/sw.js', {
scope: '/'
});
console.log('SW registered, scope:', registration.scope);
// Detect when a new SW version is waiting
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// New version is waiting show update notification to user
showUpdateBanner();
}
});
});
} catch (err) {
console.error('SW registration failed:', err);
}
});
}
</script>Always wrap registration in a "serviceWorker" in navigator check for browsers that don't support it.
Minimal service worker (sw.js) with cache-first strategy
// sw.js runs in background thread, not on the page
const CACHE_NAME = 'v1';
const PRECACHE_URLS = ['/', '/styles.css', '/app.js', '/offline.html'];
// Install: precache critical assets
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(PRECACHE_URLS))
.then(() => self.skipWaiting()) // activate immediately
);
});
// Activate: 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))
)
).then(() => self.clients.claim())
);
});
// Fetch: cache-first, fall back to network
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then(cached => {
return cached ?? fetch(event.request).then(response => {
// Cache new successful responses
if (response.ok) {
const clone = response.clone();
caches.open(CACHE_NAME).then(c => c.put(event.request, clone));
}
return response;
}).catch(() => caches.match('/offline.html'))
})
);
});This is a cache-first strategy: serve from cache, update cache in background, fall back to offline page.
Communicating between the page and service worker
// In the page send message to SW
navigator.serviceWorker.controller?.postMessage({
type: 'SKIP_WAITING'
});
// In the page receive message from SW
navigator.serviceWorker.addEventListener('message', (event) => {
if (event.data.type === 'CACHE_UPDATED') {
console.log('New content available!');
}
});
// In sw.js receive message from page
self.addEventListener('message', (event) => {
if (event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
// Reply to the page
event.source.postMessage({ type: 'CACHE_UPDATED' });
});Common Mistakes
- Registering the SW before the page has loaded use `window.addEventListener("load", ...)` to avoid competing for network during page load
- Caching API responses or dynamic data with a simple cache-first strategy stale data causes bugs; use stale-while-revalidate or network-first for dynamic content
- Forgetting `event.waitUntil()` in install/activate handlers without it, the browser may terminate the SW before async operations complete
Note
Use Workbox (by Google) for production service workers. It provides pre-built strategies (CacheFirst, NetworkFirst, StaleWhileRevalidate), precaching, and automatic cache versioning avoiding the need to write boilerplate cache management by hand.
Exercises
Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4