Skip to content

graybearo/mv3-storage

Repository files navigation

mv3-storage

Chrome extension storage wrapper for Manifest V3 (MV3) — a type-safe wrapper over chrome.storage with defaults, atomic updates, and typed subscriptions. Works with local, sync, and session areas. Zero dependencies.

npm version downloads MIT

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);
});

Why

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.

Install

pnpm add mv3-storage
# or: npm i mv3-storage
# or: yarn add mv3-storage

Usage

Declare your schema

// 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 },
  },
});

Use it anywhere

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;
});

API

createStorage<S>(options) → Storage<S>

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.

Storage<S>

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.

Storage area choice

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.

Caveat: update is not lock-protected

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.

Demo extension

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.

Related packages

Part of a small MV3 toolkit for Chrome / Edge / Firefox extensions by @graybearo:

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors