C.W.K.
Stream
Lesson 02 of 06 · published

Registering the Service Worker

~10 min · service-worker, manifest, background, clipdeck, hands-on

Level 0Extension Curious
0 XP0/54 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete
"Track 1 ended with ClipDeck as pure UI — popup-only. Track 2 begins by giving it a background. One manifest field, one file, one reload, and ClipDeck has a service worker."

One Manifest Field, One File

The manifest change is small: a single top-level background object pointing at the worker file. Three things to notice:

  • service_worker is singular. Unlike MV2 (which accepted an array of scripts), MV3 accepts exactly one file. If you need to split logic, use ES module imports.
  • The path is relative to the extension root, same as icons and popup.
  • There's no persistent field — every MV3 service worker is event-driven by definition. The MV2 persistent: true trick that kept the old background page alive is simply gone.

That's the entire manifest contract for the background context. Save the new file alongside manifest.json in clipdeck/.

The Skeleton background.js

Three listeners, no module-level state, no setInterval. The top of the file runs on every wake-up; the listeners run when their events fire. That's the entire shape of a healthy MV3 service worker — a flat file of event handlers and nothing else.

Two registrations to know about:

  • chrome.runtime.onInstalled — fires once on install, once on every extension update, and on Chrome update. Read details.reason to branch.
  • chrome.runtime.onStartup — fires when Chrome launches with the extension already installed. Useful for daily-rollup-style work that should happen once per browser session.

The console.log at the top is your eviction tracker: every time you see it print again in DevTools, the worker just cold-started.

Reload, Verify, Wake

With manifest updated and background.js saved:

  1. Reload the ClipDeck card on chrome://extensions.
  2. Open chrome://serviceworker-internals and search for ClipDeck. You should see one entry: ClipDeck's service worker, status RUNNING, a fresh registration timestamp.
  3. Wait roughly 30 seconds. Refresh. Status flips to STOPPED.
  4. Click the ClipDeck toolbar icon (any event will do — message, alarm, tab change). Refresh. Status flips back to RUNNING.

This is the entire dev loop for service workers: edit → reload → verify the worker exists → wake it on demand → watch it idle out.

Inspect the Service Worker

chrome://extensions → ClipDeck card → "Details" → "Inspect views: service worker". DevTools opens scoped to the worker. The Console shows the [ClipDeck SW] alive at ... logs from background.js's top-level execution. Network panel shows any fetch requests. Application tab shows storage (lesson 4 lives there). Sources tab lets you set breakpoints, step through listeners, inspect locals.

Two quirks worth knowing:

  • The DevTools window stays open even when the worker is evicted. The next wake will re-attach the same DevTools session.
  • If you close DevTools mid-debug, you lose breakpoints. Re-open and re-set them.

ClipDeck Now Has a Background

After this lesson ClipDeck has two surfaces — popup (UI, on-demand) and service worker (background, event-driven). Lesson 3 explores the lifecycle in depth, including how to demonstrate state loss firsthand. Lesson 4 adds chrome.storage as the survival layer. Lesson 5 wires popup ↔ SW message passing. Lesson 6 ties everything together with a visit counter — the R in CRUD warming up.

The manifest field is small. The mental model behind it is big. Every line you'll write in background.js assumes the worker can vanish between events.
Add a [SW] prefix to every console.log in background.js. When popup, content scripts, and the worker eventually all log to the same DevTools sessions (Track 3 onwards), the prefix tells you instantly which surface spoke. Tracks 1-2 set the habit cheaply, before there's anything to disambiguate.

Code

clipdeck/manifest.json — add background.service_worker + bump version·json
{
  "manifest_version": 3,
  "name": "ClipDeck",
  "version": "0.2.0",
  "description": "Selected-text clipboard you can CRUD from any page.",
  "icons": {
    "16": "icons/icon-16.png",
    "48": "icons/icon-48.png",
    "128": "icons/icon-128.png"
  },
  "action": {
    "default_popup": "popup.html",
    "default_title": "ClipDeck"
  },
  "background": {
    "service_worker": "background.js"
  }
}
clipdeck/background.js — minimal service worker·javascript
// clipdeck/background.js — ClipDeck v0.2 (Track 2 lesson 2)
// Service worker top-level — runs on every wake-up.

console.log("[ClipDeck SW] alive at", new Date().toISOString());

chrome.runtime.onInstalled.addListener((details) => {
  console.log(
    "[ClipDeck SW] onInstalled — reason:",
    details.reason,
    "previousVersion:",
    details.previousVersion
  );
});

chrome.runtime.onStartup.addListener(() => {
  console.log("[ClipDeck SW] onStartup — Chrome launched");
});

// No module-level state. No setInterval. No fetch loops.
// Every event handler is the entire unit of work.

External links

Exercise

Update clipdeck/manifest.json to include the background.service_worker field and bump version to "0.2.0". Create clipdeck/background.js with the skeleton above. Reload the ClipDeck card on chrome://extensions. Then walk these checkpoints: (1) chrome://serviceworker-internals — find ClipDeck, note status (should be RUNNING right after reload). (2) Wait 30 seconds, refresh — should flip to STOPPED. (3) Click ClipDeck toolbar icon. Refresh chrome://serviceworker-internals — back to RUNNING. (4) Open Inspect views: service worker on the ClipDeck card and confirm you see the [ClipDeck SW] alive at ... log in the Console.
Hint
If chrome://serviceworker-internals shows no ClipDeck entry, manifest didn't update — check JSON syntax (trailing comma is the usual culprit) and click reload again. If background.js has a syntax error, the Errors panel on the ClipDeck extension card will show the line. Inspect views never appears for an extension without background.service_worker, so its presence is a second confirmation the manifest took.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.