connect.storage
The extension's private, per-profile key–value store — areas, methods, and change events.
The extension's own private key–value store, isolated per profile and per extension. No other extension — and no website — can see it. This is where you keep settings, cached data, and small bits of state between sessions.
- Capability:
storage - Namespace:
connect.storage
Walkthrough
Get from nothing to reading and writing state in four steps.
- Declare the capability. Add
storageto your manifest so the user can grant it:{ "capabilities": ["storage"] } - Pick an area. Use
localfor data that should survive a restart,sessionfor throwaway/sensitive state (see Areas). - Write, then read.
setmerges an object in;getreads it back.await connect.storage.local.set({ theme: 'dark' }); const { theme } = await connect.storage.local.get('theme'); // 'dark' - React to changes with
onChangedso every surface stays in sync.
public/docs-img/extensions/storage-grant.pngAreas
There are three storage areas. Pick by how long the data should live:
| Area | Lifetime | On disk? | Use for |
|---|---|---|---|
connect.storage.local | Persisted — survives restart; cleared on uninstall. | Yes (under the profile). | Settings, cached results, anything that should outlive the session. |
connect.storage.session | In-memory — cleared on restart or uninstall. | No. | Ephemeral state, per-run scratch data, secrets you don't want on disk. |
connect.storage.sync | Persisted (separate store from local). | Yes (under the profile). | Data you'd sync across devices. v1 is device-local — same API shape as chrome.storage.sync, so code written for it works; real cross-device sync arrives later behind this same area. |
All areas expose the same object API (below). Values must be JSON-serialisable —
they round-trip through JSON.stringify, so functions, Map/Set, Date objects, and
undefined values are not preserved.
Methods
area.get(query?)
Read one or more values from an area.
How to use it
- Decide what you want back: one key (string), several (array), some with defaults
(object), or the whole area (
null). awaitthe call — it always resolves an object keyed by name.- Destructure the result, supplying a default for keys that may be absent.
Signature
connect.storage.local.get(query?) // or .session.get(query?)Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | no | A single key. Resolves { [key]: value } if present, {} if not. |
query | string[] | no | Several keys. Resolves an object with each found key. |
query | object | no | A defaults map. Resolves those keys, using the given value for any that are missing. |
query | null / omitted | no | Resolves the entire area. |
Returns — Promise<Record<string, unknown>>. Always an object keyed by name; missing
keys are simply absent (unless a defaults object supplied one).
Errors
- Rejects with
code: 'CAPABILITY_DENIED'ifstorageisn't granted in this profile.
Examples
// A single key, an array, defaults, and "everything"
await connect.storage.local.get('theme'); // { theme: 'dark' }
await connect.storage.local.get(['theme', 'count']); // { theme: 'dark', count: 3 }
await connect.storage.local.get({ theme: 'light' }); // default fills a missing key
await connect.storage.local.get(null); // the whole area// Read-modify-write with a default
const { retries = 0 } = await connect.storage.local.get({ retries: 0 });
await connect.storage.local.set({ retries: retries + 1 });area.set(items)
Write one or more values, merging into the area (existing keys not named are untouched).
How to use it
- Build an object of the keys you want to change.
awaitset— only the keys you pass are touched; the rest of the area is untouched.- Remember a write equal to the current value is a no-op (fires no
onChanged).
Signature
connect.storage.local.set(items)Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
items | Record<string, unknown> | yes | Key → value pairs to store. Values must be JSON-serialisable. |
Returns — Promise<void>.
Errors & edge cases
- Rejects with
code: 'CAPABILITY_DENIED'ifstorageisn't granted. - Writing a value equal to the current one is a no-op and fires no
onChanged. - Non-serialisable parts of a value are dropped by the JSON round-trip.
Examples
await connect.storage.local.set({ theme: 'dark', count: 3 });// session area — cleared on restart; good for a short-lived token
await connect.storage.session.set({ csrfToken: token });area.remove(keys)
Delete one or more keys.
How to use it
- Pass a single key, or an array of keys, to delete.
awaitit — removing a key that isn't there is a harmless no-op.
Signature
connect.storage.local.remove(keys)Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
keys | string | string[] | yes | A key, or an array of keys, to delete. |
Returns — Promise<void>. Removing a key that doesn't exist is a no-op (fires no event
for that key).
Example
await connect.storage.local.remove('count');
await connect.storage.local.remove(['a', 'b']);area.clear()
Remove everything in the area.
How to use it
- Call
clear()on the area you want emptied — this is irreversible. - It fires
onChangedwith each removed key (unless the area was already empty).
Signature
connect.storage.session.clear()Returns — Promise<void>.
Example
await connect.storage.session.clear();area.getBytesInUse(keys?)
Measure how much the area (or specific keys) occupies.
How to use it
- Omit
keysfor the whole area, or pass a key / array to measure just those. awaitthe number of bytes.
Signature
connect.storage.local.getBytesInUse(keys?)Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
keys | string | string[] | no | Measure only these keys. Omit for the whole area. |
Returns — Promise<number> — bytes.
Example
await connect.storage.local.getBytesInUse(); // total for the area
await connect.storage.local.getBytesInUse('theme'); // one keyconnect.storage.get(key) / connect.storage.set(key, value)
Single-key sugar over the local area, kept for backward compatibility. Prefer the
object API above for new code.
How to use it
set(key, value)to write one value intolocal.get(key)to read it back as{ [key]: value }.
Signature
connect.storage.get(key) // → Promise<{ [key]: value }>
connect.storage.set(key, value) // → Promise<void>Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
key | string | yes | The key to read or write (in local). |
value | unknown | yes (set) | JSON-serialisable value. |
Example
await connect.storage.set('token', 'abc');
await connect.storage.get('token'); // { token: 'abc' }Events
onChanged(cb)
Fires whenever a stored value changes.
How to use it
- Choose scope:
area.onChanged(cb)for one area, orconnect.storage.onChanged(cb)for both (you also getareaName). - In the callback, inspect
changes[key].newValue/oldValuefor the keys you care about. - Keep the returned function and call it to unsubscribe when you're done.
Two forms
| Form | Callback | Scope |
|---|---|---|
connect.storage.local.onChanged(cb) | cb(changes) | That one area. |
connect.storage.onChanged(cb) | cb(changes, areaName) | All areas; areaName is 'local', 'session', or 'sync'. |
changes payload
| Shape | Meaning |
|---|---|
{ [key]: { oldValue, newValue } } | The keys that changed, each with before/after. |
missing oldValue | the key was created. |
missing newValue | the key was removed. |
Returns — an unsubscribe function; call it to stop listening.
Edge cases
- A no-op
set(writing an equal value) fires nothing. - Needs the
storagecapability, like the rest of the namespace.
Example
// React to a theme change across all areas
const off = connect.storage.onChanged((changes, area) => {
if (area === 'local' && changes.theme) applyTheme(changes.theme.newValue);
});
// Scoped to one area
connect.storage.local.onChanged((changes) => console.log('local changed', changes));
// later
off();Notes
- Data is per profile and per extension — switching profiles switches the whole store; another extension can never read yours.
localis written to disk under the profile;sessionnever touches disk — prefer it for anything sensitive that doesn't need to persist.- There is no size quota enforced today, but keep values small — this is settings-and-state storage, not a database.
