"If the service worker is a function that wakes and sleeps, chrome.storage is the memory it's writing onto. Lesson 4 is about making peace with the fact that everything important must live in storage."
Two Storage Areas: local and sync
chrome.storage has multiple sub-namespaces. The two you'll use most:
chrome.storage.local— per-machine, persists until the extension is uninstalled. About 10 MB quota in MV3. Promise-based async API. The default choice for ClipDeck's clips, visit counts, and similar accumulating data.chrome.storage.sync— synced across the user's signed-in Chrome instances. About 100 KB total, with a per-item cap around 8 KB. Use for user preferences (theme, hotkey config), NOT for accumulating data.
Less common but worth knowing: chrome.storage.session (added 2023) survives worker eviction but dies with the browser session. chrome.storage.managed is read-only and populated by enterprise policy.
The Async API
All chrome.storage operations are async. As of MV3 they return Promises (older callback signatures still work in parallel). Six methods cover almost everything:
get(key | keys[] | undefined)— read one key, several keys, or everythingset(object)— merge the given object into storage (idempotent; unrelated keys untouched)remove(key | keys[])— delete keysclear()— wipe everything in this storage areagetBytesInUse(key | keys[] | undefined)— quota checkonChanged.addListener(fn)— react to writes from any context
Calling set with an object merges keys; it does NOT replace the whole storage. So you can write { visitCount: 5 } without affecting { clips: [...] }. That makes the storage layer composable across features.
JSON-Only Values
chrome.storage serializes values to JSON. Practical consequences:
- Plain objects, arrays, strings, numbers, booleans, null — all fine.
Dateobjects → converted to ISO strings on the way in; you get back a string on the way out. Lose the type.Map,Set→ become{}or empty arrays. Convert explicitly toObject.fromEntries(map)or[...set]before storing.- Functions, class instances with methods, circular references — silent corruption or outright failure.
Stick to plain JSON-compatible shapes. Convert exotic types to plain values explicitly on the way in, reconstruct them on the way out.
Listening for Changes
chrome.storage.onChanged fires on every write in every context — popup, side panel, service worker, content script. The popup can react instantly to a write the worker made, no message passing required:
This is the alternative to chrome.runtime.sendMessage for read-state updates. The popup subscribes to onChanged, the SW writes to storage, the popup re-renders. No round-trip, no "is the worker awake" question.
ClipDeck's First Storage Round-Trip
Lesson 6 will use storage for ClipDeck's visit counter. The skeleton looks like the second code block below. That's the read-do-write-read pattern in its purest form. Stateless at the module level. ClipDeck's clip list (Track 3 and beyond) follows the same shape — read array from storage, append, write back. Edit and delete (Track 7) follow the same shape — read, mutate, write.
Once you internalize this single pattern, the rest of ClipDeck is mostly variations on its theme.