service-worker
Original:🇺🇸 English
Translated
Service Worker API implementation guide — registration, lifecycle management, caching strategies, push notifications, and background sync. Use when: (1) creating or modifying service worker files (sw.js), (2) implementing offline-first caching (cache-first, network-first, stale-while-revalidate), (3) setting up push notifications or background sync, (4) debugging service worker registration, scope, or update issues, (5) implementing navigation preload, (6) user mentions 'service worker', 'sw.js', 'offline support', 'cache strategy', 'push notification', 'background sync', 'workbox alternative', or 'PWA caching'.
2installs
Sourcejgamaraalv/ts-dev-kit
Added on
NPX Install
npx skill4agent add jgamaraalv/ts-dev-kit service-workerTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Service Worker
Table of Contents
- Constraints
- Lifecycle
- Registration
- Install Event — Pre-cache Assets
- Activate Event — Clean Up Old Caches
- Fetch Event — Intercept Requests
- Navigation Preload
- Updating a Service Worker
- Communicating with Pages
- Common Pitfalls
- Push Notifications & Background Sync
- API Quick Reference
- Next.js Integration
- DevTools
Constraints
- HTTPS required (localhost exempt for dev)
- No DOM access — runs on separate thread
- Fully async — no synchronous XHR, no localStorage
- No dynamic — only static
import()statementsimport - Scope defaults to the directory containing the SW file
- refers to
selfServiceWorkerGlobalScope
Lifecycle
register() → Download → Install → [Wait] → Activate → Fetch control- Register from main thread via
navigator.serviceWorker.register() - Install event fires once — use to pre-cache static assets
- Wait — new SW waits until all tabs using old SW are closed (skip with )
self.skipWaiting() - Activate event fires — use to clean up old caches
- Fetch events start flowing — SW controls page network requests
A document must reload to be controlled (or call during activate).
clients.claim()Registration
js
// main.js — register from the page
if ("serviceWorker" in navigator) {
const reg = await navigator.serviceWorker.register("/sw.js", { scope: "/" });
// reg.installing | reg.waiting | reg.active
}Scope rules:
- SW at can control
/sw.jsand all subpaths/ - SW at can only control
/app/sw.jsby default/app/ - Broaden scope with response header
Service-Worker-Allowed
Install Event — Pre-cache Assets
js
// sw.js
const CACHE_NAME = "v1";
const PRECACHE_URLS = ["/", "/index.html", "/style.css", "/app.js"];
self.addEventListener("install", (event) => {
event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE_URLS)));
});waitUntil(promise)Activate Event — Clean Up Old Caches
js
self.addEventListener("activate", (event) => {
event.waitUntil(
caches
.keys()
.then((keys) =>
Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key))),
),
);
});Fetch Event — Intercept Requests
js
self.addEventListener("fetch", (event) => {
event.respondWith(caches.match(event.request).then((cached) => cached || fetch(event.request)));
});respondWith(promise)ResponseFor caching strategy patterns (cache-first, network-first, stale-while-revalidate), see references/caching-strategies.md.
Navigation Preload
Avoid the startup delay when a SW boots to handle a navigation:
js
self.addEventListener("activate", (event) => {
event.waitUntil(self.registration?.navigationPreload.enable());
});
self.addEventListener("fetch", (event) => {
event.respondWith(
(async () => {
const cached = await caches.match(event.request);
if (cached) return cached;
const preloaded = await event.preloadResponse;
if (preloaded) return preloaded;
return fetch(event.request);
})(),
);
});Updating a Service Worker
- Browser byte-compares the SW file on each navigation (or every 24h)
- New version installs in background while old version still serves
- Increment the cache name (e.g., →
v1) in the new versionv2 - Delete old caches in the handler
activate - Call in
self.skipWaiting()to activate immediatelyinstall - Call in
self.clients.claim()to take control of open pagesactivate
Communicating with Pages
js
// Page → SW
navigator.serviceWorker.controller.postMessage({ type: "SKIP_WAITING" });
// SW → Page (via Clients API)
const clients = await self.clients.matchAll({ type: "window" });
clients.forEach((client) => client.postMessage({ type: "UPDATED" }));
// SW listens
self.addEventListener("message", (event) => {
if (event.data?.type === "SKIP_WAITING") self.skipWaiting();
});Common Pitfalls
- Response cloning — before both caching and returning, since body streams can only be read once
response.clone() - Opaque responses — cross-origin fetches without CORS return opaque responses (status 0). will refuse them. Use
cache.add()but you can't inspect the responsecache.put() - waitUntil timing — call synchronously within the event handler, not inside an async callback
event.waitUntil() - Scope ceiling — a SW cannot control URLs above its own directory unless header is set
Service-Worker-Allowed - No state persistence — the SW may terminate at any time when idle. Don't store state in global variables — use Cache API or IndexedDB
Push Notifications & Background Sync
For push subscription, handling push events, and background sync implementation, see references/push-and-sync.md.
API Quick Reference
For detailed interfaces (, , , , , ), see references/api-reference.md.
CacheCacheStorageFetchEventClientsServiceWorkerRegistrationServiceWorkerGlobalScopeNext.js Integration
In Next.js, place the service worker file in . is intentionally plain JS (not processed by Next.js build pipeline). Register it from a client component:
public/sw.jspublic/sw.jstsx
"use client";
import { useEffect } from "react";
export function ServiceWorkerRegistrar() {
useEffect(() => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}
}, []);
return null;
}Add to root layout. Next.js serves files at the root, so scope covers .
public//sw.js/DevTools
- Chrome: or Application > Service Workers
chrome://inspect/#service-workers - Firefox: or Application > Service Workers
about:debugging#/runtime/this-firefox - Edge: or Application > Service Workers
edge://inspect/#service-workers
Unregister, update, and inspect caches from the Application panel. Use "Update on reload" checkbox during development.