Skip to content
Development
Skill

/web-pwa-service-workers

Service Worker lifecycle, caching strategies, offline patterns, update handling, precaching, runtime caching

From plugin
agents-inc-skills
24200 skills
Install
$ npx -y skills add agents-inc/skills --skill web-pwa-service-workers --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/web-pwa-service-workers

Context preview

The summary Claude sees to decide when to auto-load this skill.

Service Worker lifecycle, caching strategies, offline patterns, update handling, precaching, runtime caching

SKILL.md

web-pwa-service-workers.SKILL.md
name: web-pwa-service-workers
description: Service Worker lifecycle, caching strategies, offline patterns, update handling, precaching, runtime caching

Service Worker Patterns

> **Quick Guide:** A service worker is a programmable network proxy with its own lifecycle: > install precaches, activate cleans up, fetch intercepts. Match the caching strategy to the > content — cache-first for hashed assets, network-first for HTML, stale-while-revalidate for > non-critical API reads. Two details cause most bugs: a response body can be read once, so > `cache.put(request, response.clone())`, and a new worker waits until every tab using the old one > closes, so updates need explicit detection and a user-triggered `skipWaiting`.

**Detailed Resources:**

  • [examples/core.md](examples/core.md) — registration, the full lifecycle template, the four strategy implementations, message types
  • [examples/caching.md](examples/caching.md) — background refresh, expiry timestamps, per-endpoint routing, navigation preload, quota cleanup
  • [examples/updates.md](examples/updates.md) — version messaging, aggressive/deferred/idle updates, progressive rollout, cache migration
  • [reference.md](reference.md) — strategy-by-content-type table, lifecycle and Cache API lookup, update-strategy selection, review checklist

---

Which path applies

  • **Precached app shell** — the HTML is one cached document and routing happens client-side. Serve

it cache-first, and skip navigation preload; there is nothing to preload.

  • **Server-rendered or authenticated pages** — each navigation is a fresh document. Serve

network-first with a timeout and an offline fallback, and enable navigation preload so the request starts before the worker boots. Follow [examples/caching.md](examples/caching.md) Pattern 8.

---

<critical_requirements>

Before writing service worker code

**Wrap every async lifecycle task in `event.waitUntil()`.** It keeps the worker alive until the promise settles; without it the browser is free to terminate mid-precache.

**Put a version in every cache name and delete the non-current ones during activate.** That is what makes an upgrade clean and keeps storage from growing without bound.

**Clone before caching — `cache.put(request, response.clone())`.** A response body can be consumed once, so caching the original leaves the client with an empty response.

**Let the client decide when a waiting worker takes over.** Detect the waiting worker, tell the user, and call `skipWaiting()` in response to their message, so behaviour never changes underneath an open session.

**Give every fetch path a fallback.** A precached `offline.html` for navigations and a constructed `Response` as the last resort turn a network failure into a page you wrote.

</critical_requirements>

---

**Auto-detection:** navigator.serviceWorker, serviceWorker.register, ServiceWorkerGlobalScope, sw.js, sw.ts, self.skipWaiting, clients.claim, event.waitUntil, event.respondWith, event.preloadResponse, caches.open, caches.match, cache.addAll, CacheStorage, navigationPreload, updateViaCache, controllerchange, updatefound, precache

**Applies to:**

  • Intercepting and answering network requests from a worker thread
  • Choosing and implementing a caching strategy per content type
  • Precaching an app shell and cleaning up superseded caches
  • Detecting a waiting worker and applying the update on the user's terms
  • Serving an offline fallback when both cache and network fail

**Handled elsewhere:**

  • Structured local data that the application owns and writes — a response cache stores what the

server said, which is a different lifetime from records a user edits offline

  • Queueing mutations made while disconnected and reconciling them later
  • Push notification content and permission flows — the worker's `push` event is a delivery hook,

and what to show is a product decision

---

<philosophy>

Philosophy

A service worker is a proxy, not a plugin: once installed it sees every request in its scope, and anything it fails to answer, it breaks.

The lifecycle exists to stop two versions running at once. A new worker installs immediately but **waits** until every client controlled by the old one has gone, so one page never runs half the old assets and half the new ones.

Registration → Download → Install → Waiting → Activate → Fetch
                            ↓          ↓
                     (skipWaiting)  (claim)

`skipWaiting()` and `clients.claim()` are the two escapes from that guarantee, and both are opt-in for a reason. Reach for them when the user has asked for the update, or when a fix is urgent enough to be worth a mid-session change.

</philosophy>

---

<patterns>

Core patterns

Pattern 1: Registration with update detection

Register with feature detection, check for updates periodically, and track the waiting worker so the UI can offer the update.

const registration = await navigator.serviceWorker.register("/sw.js", {
  scope: "/",
  updateViaCache: "none", // ask the server for the worker script every time
});

setInterval(() => registration.update(), UPDATE_CHECK_INTERVAL_MS);

registration.addEventListener("updatefound", () => {
  const installing = registration.installing;
  installing?.addEventListener("statechange", () => {
    const isWaiting =
      installing.state === "installed" && navigator.serviceWorker.controller;
    if (isWaiting) notifyUpdateAvailable();
  });
});

Full code: [examples/core.md](examples/core.md)

Pattern 2: Lifecycle handlers

Precache in install, delete superseded caches in activate, and take `skipWaiting` as a message rather than calling it unconditionally.

self.addEventListener("install", (event: ExtendableEvent) => {
  event.waitUntil(
    caches.open(CACHES.static).then((cache) => cache.addAll(PRECACHE_URLS)),
  );
});

self.addEventListener("activate", (event: ExtendableEvent) => {
  event.waitUntil(
    (async () => {
      cons
Read more
Ships withagents-inc-skills

The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?

Get the whole plugin

Other skills on agents-inc-skills.