Connect+Documentation
ExtensionsAPI reference

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.

  1. Declare the capability. Add storage to your manifest so the user can grant it:
    { "capabilities": ["storage"] }
  2. Pick an area. Use local for data that should survive a restart, session for throwaway/sensitive state (see Areas).
  3. Write, then read. set merges an object in; get reads it back.
    await connect.storage.local.set({ theme: 'dark' });
    const { theme } = await connect.storage.local.get('theme'); // 'dark'
  4. React to changes with onChanged so every surface stays in sync.
Screenshot
caption: The install/permission prompt where the user grants the `storage` capability for this profile. Capture the profile permission dialog with `storage` listed.
drop the image at public/docs-img/extensions/storage-grant.png

Areas

There are three storage areas. Pick by how long the data should live:

AreaLifetimeOn disk?Use for
connect.storage.localPersisted — survives restart; cleared on uninstall.Yes (under the profile).Settings, cached results, anything that should outlive the session.
connect.storage.sessionIn-memory — cleared on restart or uninstall.No.Ephemeral state, per-run scratch data, secrets you don't want on disk.
connect.storage.syncPersisted (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

  1. Decide what you want back: one key (string), several (array), some with defaults (object), or the whole area (null).
  2. await the call — it always resolves an object keyed by name.
  3. Destructure the result, supplying a default for keys that may be absent.

Signature

connect.storage.local.get(query?)     // or .session.get(query?)

Parameters

ParameterTypeRequiredDescription
querystringnoA single key. Resolves { [key]: value } if present, {} if not.
querystring[]noSeveral keys. Resolves an object with each found key.
queryobjectnoA defaults map. Resolves those keys, using the given value for any that are missing.
querynull / omittednoResolves the entire area.

ReturnsPromise<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' if storage isn'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

  1. Build an object of the keys you want to change.
  2. await set — only the keys you pass are touched; the rest of the area is untouched.
  3. Remember a write equal to the current value is a no-op (fires no onChanged).

Signature

connect.storage.local.set(items)

Parameters

ParameterTypeRequiredDescription
itemsRecord<string, unknown>yesKey → value pairs to store. Values must be JSON-serialisable.

ReturnsPromise<void>.

Errors & edge cases

  • Rejects with code: 'CAPABILITY_DENIED' if storage isn'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

  1. Pass a single key, or an array of keys, to delete.
  2. await it — removing a key that isn't there is a harmless no-op.

Signature

connect.storage.local.remove(keys)

Parameters

ParameterTypeRequiredDescription
keysstring | string[]yesA key, or an array of keys, to delete.

ReturnsPromise<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

  1. Call clear() on the area you want emptied — this is irreversible.
  2. It fires onChanged with each removed key (unless the area was already empty).

Signature

connect.storage.session.clear()

ReturnsPromise<void>.

Example

await connect.storage.session.clear();

area.getBytesInUse(keys?)

Measure how much the area (or specific keys) occupies.

How to use it

  1. Omit keys for the whole area, or pass a key / array to measure just those.
  2. await the number of bytes.

Signature

connect.storage.local.getBytesInUse(keys?)

Parameters

ParameterTypeRequiredDescription
keysstring | string[]noMeasure only these keys. Omit for the whole area.

ReturnsPromise<number> — bytes.

Example

await connect.storage.local.getBytesInUse();        // total for the area
await connect.storage.local.getBytesInUse('theme'); // one key

connect.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

  1. set(key, value) to write one value into local.
  2. 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

ParameterTypeRequiredDescription
keystringyesThe key to read or write (in local).
valueunknownyes (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

  1. Choose scope: area.onChanged(cb) for one area, or connect.storage.onChanged(cb) for both (you also get areaName).
  2. In the callback, inspect changes[key].newValue / oldValue for the keys you care about.
  3. Keep the returned function and call it to unsubscribe when you're done.

Two forms

FormCallbackScope
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

ShapeMeaning
{ [key]: { oldValue, newValue } }The keys that changed, each with before/after.
missing oldValuethe key was created.
missing newValuethe 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 storage capability, 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.
  • local is written to disk under the profile; session never 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.

On this page