Chrome extension storage wrapper for Manifest V3 (MV3) — a type-safe wrapper over
chrome.storagewith defaults, atomic updates, and typed subscriptions. Works withlocal,sync, andsessionareas. Zero dependencies.
interface MyStore {
count: number;
user: { id: string; name: string } | null;
prefs: { theme: "light" | "dark"; lang: string };
}
import { createStorage } from "mv3-storage";
const store = createStorage<MyStore>({
area: "local",
defaults: {
count: 0,
user: null,
prefs: { theme: "light", lang: "en" },
},
});
const count = await store.get("count"); // typed: number
await store.set("user", { id: "1", name: "Ada" }); // typed: User | null
await store.update("count", (n) => n + 1); // typed: number → number
const all = await store.getAll(); // typed: MyStore
const off = store.subscribe("user", (next, prev) => {
console.log("user changed", prev, "→", next);
});chrome.storage.local.get returns Promise<unknown>. Reading anything
back means casting or hand-rolling type assertions on every call site.
This package wraps the API with a single typed schema, automatic defaults,
atomic update(key, fn), and a properly-typed subscription API.
pnpm add mv3-storage
# or: npm i mv3-storage
# or: yarn add mv3-storage// src/shared/store.ts
import { createStorage } from "mv3-storage";
export interface AppStore {
authToken: string | null;
recentSearches: string[];
preferences: { theme: "light" | "dark"; notifications: boolean };
}
export const store = createStorage<AppStore>({
area: "sync", // or "local" or "session"
defaults: {
authToken: null,
recentSearches: [],
preferences: { theme: "light", notifications: true },
},
});import { store } from "@/shared/store";
const token = await store.get("authToken"); // string | null
await store.set("authToken", "xyz");
await store.update("recentSearches", (arr) => [...arr, "react", "vite"].slice(-10));
// React to changes
const off = store.subscribe("preferences", (next) => {
document.body.dataset.theme = next.theme;
});| Option | Type | Default | Notes |
|---|---|---|---|
area |
"local" | "sync" | "session" |
"local" |
Which chrome.storage area to use. |
defaults |
S |
required | Returned when a key has no stored value. Defines the schema's keys. |
| Method | Returns | Notes |
|---|---|---|
get(key) |
Promise<S[K]> |
Returns the default when no value is stored. |
set(key, value) |
Promise<void> |
|
update(key, fn) |
Promise<S[K]> |
fn receives the current value and returns (or resolves to) the next. |
remove(key) |
Promise<void> |
Subsequent get returns the default. |
getAll() |
Promise<S> |
All keys merged with defaults. |
clear() |
Promise<void> |
Clears the entire area — including keys not in your schema. Use carefully. |
subscribe(key, fn) |
() => void |
Notified on every change to key in the chosen area. |
subscribeAll(fn) |
() => void |
Notified once per change batch with a Partial<S> of the new values. |
| Area | Persistence | Quota | Notes |
|---|---|---|---|
local |
Until uninstall | ~10 MB | Per-device. The usual choice. |
sync |
Synced via Google account | ~100 KB | Smaller quota; synced across devices. |
session |
Cleared on browser restart | ~10 MB | In-memory; useful for ephemeral state. MV3-only. |
store.update(key, fn) reads, calls fn, then writes. If two callers
race on the same key, the second wins. For most extension use cases this
is fine (popup-only edits, single-SW writes); for genuinely concurrent
writes, do the merge in your handler.
A runnable demo lives in example/. The popup edits a typed
counter and preference settings; changes persist across popup re-opens
and surface in the SW console via subscribe.
cd example
pnpm install
pnpm build
# then load example/dist/ via chrome://extensions → "Load unpacked"See example/README.md for full instructions.
Part of a small MV3 toolkit for Chrome / Edge / Firefox extensions by @graybearo:
mv3-message-router— typed messaging between popup, content, and service workermv3-keepalive— service-worker keepalive + durable alarmsmv3-content-bridge— content-script ↔ page-context typed bridgechrome-extension-vite-react— Vite + React + TS MV3 starterchrome-extension-webpack-react— webpack + React + TS MV3 starterawesome-mv3— curated list of MV3 tools, libraries, and resources
MIT — see LICENSE.