Conflict resolution UI
Every handshake-replay store can produce a conflict on reconnect. Rather than building a resolution UI per store, Flux funnels all of them through one callback and one array — this page covers wiring that up, what each button actually calls, and the one place a shallow-merge default isn't enough.
- At least one store registered with replayStrategy: 'handshake' — see core-concepts/conflict-resolution
- @tsworldtech/flux-react or @tsworldtech/flux-next installed, if using the built-in FluxConflictPanel
- Familiarity with api/get-active-conflicts and api/resolve-unified-conflict
Why one panel instead of one per store
The full wiring
useFlux()'s activeConflicts already mirrors the engine's live register — no manual subscription or state management needed.What actually produces a conflict
replayStrategy: 'handshake' stores can ever reach this panel — fire_and_forget stores have no server-state comparison to conflict against in the first place.What each button actually calls
resolveUnifiedConflict().The Merge view's shallow-spread limit
QueueConfig.mergeHandler only applies to dedupeStrategy: 'replace' — merging two queued entries before either is sent. It has no role in resolving a conflict against server state. For nested-object conflicts, build custom merge logic inside queueConfig.onConflict instead.
Discard vs. resolve — two different calls
Quick path — import it as-is
Why this one is worth copying instead
Full source — copy this into your project
Everything driven by onResolve/onAbandon stays identical once copied — only the visual layer is meant to change.
A licensing note
For the two-pass handshake mechanics behind every conflict shown here, see core-concepts/conflict-resolution. For the register's internal lifecycle, see api/get-active-conflicts and api/resolve-unified-conflict. For the offline upload pattern, which uses a similar copy-vs-import tradeoff, see offline/upload-pattern.