"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_workeris singular. Unlike MV2 (which accepted an array ofscripts), 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
iconsandpopup. - There's no
persistentfield — every MV3 service worker is event-driven by definition. The MV2persistent: truetrick 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. Readdetails.reasonto 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:
- Reload the ClipDeck card on
chrome://extensions. - Open
chrome://serviceworker-internalsand search for ClipDeck. You should see one entry: ClipDeck's service worker, status RUNNING, a fresh registration timestamp. - Wait roughly 30 seconds. Refresh. Status flips to STOPPED.
- 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.
[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.