Offline Write Queue
What happens to a write when the network is not there to receive it, what happens when the server says no, how deletions fit the same model as creates and updates, and the one category of write that should never go through this system at all.
When a channel is registered with a queueConfig, any write to it that cannot reach the server is captured and held rather than discarded. It leaves the queue in only three ways: the server accepts it, the server confirms it already has it, or the user explicitly discards it. If the server answers with an error, the write is kept and shown to the user as a failed change they can retry or discard. Your UI code for "save this note" does not need an offline branch; the same call works identically online or off.
Turning a channel into an offline safe write path
queueConfig to any registration and every write against that channel becomes offline safe. Nothing else about the registration changes.replayEndpoint is where the queued write gets sent once it is safe to send. Until then, it sits captured locally — surviving a page refresh, a browser crash, and even a tab freeze, so nothing is lost between the moment the user hits save and the moment the network is actually available to hear about it.Duplicate writes are handled for you
dedupeField and dedupeStrategy let you decide what happens when that occurs while an earlier write for the same identifier is still queued.'reject' blocks the second write outright — the first one queued wins. 'replace' merges the new payload into the still queued entry, so the most recent input wins without producing two separate requests once the connection returns.Storage is resilient across three tiers
Multiple open tabs never duplicate a write
Queuing deletions
DELETE is a queue entry like any other — the same three layer storage, the same dedupe machinery, the same cross tab sync, and the same failed state if the server rejects it. The only thing that changes is the method field on the entry:onReplaySuccess receives the entry method, so you can dispatch the right confirmation shape once a queued delete actually lands:Give the delete its own dedupeKey (the record id is usually enough) so a repeated tap collapses into one queued DELETE rather than two. This is independent from — and does not replace — the pending create purge above, which is about a different record lifecycle stage entirely.
When the server says no: failed writes
queueConfig let your app respond. onFailed tells you an entry just became failed. onRollback tells you the user gave up on one, which is your cue to revert the optimistic change in the UI:Earlier versions deleted a write on a 4xx response and called onRollback immediately. Failed writes are now kept, so onRollback fires only when the user discards one through discardFailed(). If you reverted optimistic UI inside onRollback before, that still works, it just happens at discard time instead of at failure time. The older onFailure callback still fires alongside onFailed, but onFailed is the one to use.
Building a failed changes panel
subscribeToFailedWrites() hands you the full list of failed entries on subscribe and again on every change, including changes made in other tabs and after a page reload. Each entry carries its failure record, so you can show what the server said.discardFailed() with refetch: true also reloads that channel from the server, because the locally cached copy still contains an edit the server never accepted. Use it while online, since it clears the cached copy before the fresh one arrives.How failed writes get retried
api/replay.Confirm before logout
The offline write queue is built for writes where "send it a little later" is an acceptable outcome — a saved draft, a preference change, a beneficiary added to an account. It is deliberately not built, and should never be used, for financial ledger writes: payments, wire transfers, direct debits, or any operation where a duplicate send or a delayed send against changed conditions has real financial consequences.
There is no configuration on the queue that makes this safe. A queued payment that replays after reconnecting could execute against a balance, a rate, or an authorization that is no longer valid the moment it was queued under. A failed payment kept for a Retry button has the same problem. The correct pattern is to keep payment endpoints entirely outside the queue system and instead block the action at the UI level while the app is offline — failing fast and visibly, rather than deferring silently.
queueConfig at all:isOnline from useFlux() — the same flag every other part of your app can read to know whether the device currently has a connection:See api/replay for exactly when queued writes are sent, how batching and idempotency work, and the retry rules, core_concepts/conflict_resolution for what happens when a queued write for a non financial record conflicts with a change made on the server while the user was offline, core_concepts/mutation_pipeline for shaping rapid fire writes — like autosave — before they ever reach the queue, and core_concepts/lib_flux_structure for a full reference lib/flux.ts file showing all of this wired together.