Zustand
Zustand is Flux's native state manager — no adapter, no wrapper, no glue code. Pass the store hook directly to register() and Flux calls store.setState() on it directly.
- A Zustand store created with create() — see Zustand's docs if you're new to it
- Familiarity with register() — see api/register
A plain Zustand store with dual-payload support
setState to handle both initial array payloads (from cold-boot IndexedDB hydration and bootstrap) and single live delta frames (CREATE, sparse field UPDATE, and DELETE):Registering it
store field takes the Zustand hook directly — no createXAdapter() call in between, unlike Redux, Jotai, or Stunk.Why Zustand needs no adapter
hydrateState or a fallback setState call. Zustand's hook already exposes setState natively, so Flux's default fallback path works on it unmodified — there is nothing an adapter needs to translate.Handling sparse updates with hydrateState (alternative)
hydrateState inside the registration instead:Deleting a record: online vs offline
DELETE frames (Step 1) only covers the intake side. The other half is the deletion flow itself — how a delete gets from a click in your UI to a confirmed removal on the server, whether or not the device is connected at that moment. The pattern mirrors create/update exactly: try direct, fall back to the queue, confirm once replayed.If a record was created offline and never replayed, there is nothing server-side to delete yet — only a queued CREATE. Deleting it locally and purging that pending CREATE is enough; queuing a DELETE for a record the server has never seen would just produce a confusing 404 on replay.
Combining with scope
scope exactly like any other state manager — the store layer is completely independent of multi-tenant scoping.Local writes: dispatchLocalUpdate()
flux.dispatchLocalUpdate(channel, data, options?) and it goes through the exact same intake path as bootstrap and realtime data — including, for a native Zustand registration, the exact same unmodified store.setState(data) call from Step 3. No adapter-specific handling is needed here at all.Call dispatchLocalUpdate only after your server has accepted the mutation. It is not an optimistic-update mechanism — it persists to IDB and mirrors to other tabs immediately, so it should never carry local state that has not actually been saved.
Zustand vs. other state managers
Because Zustand hooks subscribe components to state slices using selector functions, invoking useTasksStore.setState() directly from Flux dispatches updates solely to components that observe the mutated records, eliminating unnecessary component subtree renders.
For Redux, see state_managers/redux. For Jotai, see state_managers/jotai. For Stunk, see state_managers/stunk. For the full offline write queue, see core_concepts/offline_queue. For every other registration field alongside store, see api/register.