Skip to content

@stainless-code/persist

Hydration-aware persistence for any reactive store — zero-dep persistSource core; codecs, backends, cross-tab transport, source + framework hydration adapters ship as opt-in recipes

persist-crosstab

59 linesSource

Cross-tab sync for backends without storage events via @stainless-code/persist/transport/crosstab (createBroadcastCrossTab). Wrap storage + pass crossTabEventTarget; call close() on teardown.

BroadcastChannel cross-tab transport

For backends with no storage events (IndexedDB). Must wrap(storage) and pass crossTabEventTarget with crossTab: true. Posts storageArea: null (key-only match — each tab owns its backend instance). Returns undefined if BroadcastChannel missing. Call close() on teardown.

Install

shell
bun add @stainless-code/persist idb-keyval @tanstack/store

Example peers: idb-keyval (IDB backend) + @tanstack/store (source). Swap either for your stack.

Minimal wiring

ts
import { Store } from "@tanstack/store";
import { createIdbStorage } from "@stainless-code/persist/backends/idb";
import { persistStore } from "@stainless-code/persist/sources/tanstack-store";
import { createBroadcastCrossTab } from "@stainless-code/persist/transport/crosstab";

const store = new Store({ theme: "light" });
const bridge = createBroadcastCrossTab({ channelName: "app:prefs" })!;
const persist = persistStore(store, {
  name: "app:prefs:v1",
  storage: bridge.wrap(createIdbStorage()!),
  crossTab: true,
  crossTabEventTarget: bridge.crossTabEventTarget,
});
// teardown: persist.destroy(); bridge.close();

localStorage can use crossTab: true alone — this bridge is for IDB-like backends.

Common mistakes

  • crossTab: true alone on IDB — no native events.
  • Forgetting wrap or close.
  • Expecting storageArea reference equality across tabs.

API surface

  • createBroadcastCrossTab({ channelName }) → { crossTabEventTarget, wrap, close } | undefined

See also: persist-idb.