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

55 linesSource

Persist with IndexedDB via @stainless-code/persist/backends/idb (createIdbStorage / idbStateStorage). Use for large or structured-clone state; pair with useHydrated and BroadcastChannel cross-tab.

IndexedDB backend

createIdbStorage uses structured clone + identityCodec — Set/Map/Date work without seroval. Fully async → gate UI. No storage events → use persist-crosstab for multi-tab.

Install

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

Peer: idb-keyval >=4.0.0.

Minimal wiring

ts
import { createIdbStorage } from "@stainless-code/persist/backends/idb";

const storage = createIdbStorage<Prefs>();

Custom codec: createStorage(() => idbStateStorage(store), codec, opts). Encrypt wraps the backend, not the codec arg — createStorage(() => createEncryptedStorage(() => idbStateStorage(), { key })!, codec, opts) (see persist-encrypted).

Gotchas

  • clearCorruptOnFailure is inert on the default identity path (identity never throws).
  • IDB missing → reject on first use (onError "hydrate"), not at construct.
  • crossTab: true alone is useless on IDB — no storage events; use persist-crosstab (wrap + crossTabEventTarget).

Common mistakes

  • Layering JSON/seroval on default IDB when structured clone already covers the graph.
  • Skipping useHydrated.
  • Expecting native storage events.

API surface

  • createIdbStorage(options?) · idbStateStorage(store?)

See also: persist-crosstab; *-persist for UI gates.