Glossary

Definition

clients.claim()

Short definition

self.clients.claim() is called inside the activate event of a service worker to make it start controlling every open page on the origin right away, rather than the default behaviour where a page keeps being controlled by whatever worker was active when it first loaded until the next navigation.

Reviewed July 28, 2026

Typical usage: self.addEventListener("activate", (event) => { event.waitUntil(clients.claim()); });, almost always paired with skipWaiting() in the install handler so the whole update reaches every open tab as fast as possible.

Without clients.claim(), a page loaded before the new worker activated keeps talking to the old worker instance for its fetch and message events until it is reloaded or navigated, even though a newer worker is technically active for the origin.

clients.claim() returns a promise that resolves once all matching clients are claimed, which is why it should be wrapped in event.waitUntil() so the activate event does not resolve prematurely.

The very first time a service worker registers on a page, that page is never controlled by it (nor by clients.claim()) — control only ever starts on the next navigation or via claim() for pages loaded under a previous worker version.

Related terms
skipWaiting()
self.skipWaiting() is called inside a service worker's install (or activate) handler to skip the normal waiting state and activate immediately, even while older pages controlled by a previous worker are still open. It is usually paired with clients.claim() so the new worker also starts controlling those pages right away.
activate event (service worker)
The activate event fires after a service worker moves from "installed" to "activating", typically once the old worker's clients have all closed or skipWaiting() was called. Handlers commonly use it to clean up outdated Cache Storage entries left behind by earlier versions of the app shell.
Service worker update cycle
Browsers check a registered service worker's script for updates on every navigation to a page in scope, and at least every 24 hours in the background, by re-fetching the script URL and comparing it byte-for-byte against the currently installed version. Any difference — even a single changed character or comment — triggers a fresh install/waiting/activate cycle for a new worker instance.
Service worker registration
Registration happens when a page calls navigator.serviceWorker.register("/sw.js", { scope: "/app/" }). The scope option (defaulting to the directory the script is served from) determines which pages the worker is allowed to control — a worker at /sw.js served with no scope option can normally only control /*, but serving it with the Service-Worker-Allowed HTTP header lets it claim a scope above its own script location.
Cache Storage API
The Cache Storage API, exposed as the global caches object, lets a service worker (or page) store Request/Response pairs in named caches, e.g. caches.open("shell-v1").then((cache) => cache.put("/logo.png", response)). Unlike the HTTP cache, it is fully scriptable, has no automatic eviction schedule tied to headers, and persists until explicitly deleted or the storage quota is exceeded.
Cache-first strategy
Cache-first is a fetch-handling strategy: event.respondWith(caches.match(event.request).then((cached) => cached ?? fetch(event.request))). It serves cached content instantly and skips the network entirely when a match exists, making it ideal for versioned, immutable assets such as hashed JS/CSS bundles or app-shell icons that never change once deployed.