Core Concepts
Conflict resolution
What happens when an offline edit and a server-side change land on the same record. This page covers the two-pass handshake that detects it, the priority chain that decides what to do about it, and the one thing conflict resolution is never allowed to touch.
Prerequisites
- Familiarity with core-concepts/offline-queue — conflicts only ever apply to queued mutations
- For wiring a resolution panel to this mechanism, see offline/conflict-ui
1
Why a conflict is even possible
Exactly one shape of event produces a conflict: an offline edit and a server-side change landing on the same record during the same disconnected window.
typescript
2
Two strategies, one fork in the road
Set once, per store, at registration. The cost of guessing wrong is asymmetric — worth defaulting to
handshake when unsure.typescript
3
The two-pass mechanics
The entire handshake, end to end. The key property: this costs exactly 2 network requests regardless of queue size — 2 for one entry, 2 for a thousand.
typescript
4
The conflict priority chain
Every conflicted entry runs through this in order and stops at the first stage that handles it — worth knowing exactly where a given conflict will land before wiring anything up.
typescript
A frozen entry has no automatic timeout
Once an entry reaches the universal register (Stage 2), it stays frozen in IDB until resolveUnifiedConflict() is explicitly called — there's no fallback that fires on its own after some delay.
5
conflictStrategy — the automatic fallback
Only reached if neither of the first two stages is configured.
'merge' in particular is a deliberate dead end, not a shortcut — worth reading closely.typescript
6
Clock skew correction
Every timestamp in the manifest is corrected before it's sent — without this, a device with a drifting clock generates false conflicts out of nothing.
typescript
7
The atomic lock — why multi-tab doesn't race
A subtlety that only shows up with more than one tab open. Without this, two tabs can both believe they're clear to write at the same time.
typescript
8
When the check itself fails
A failed revalidation and a missing one are treated very differently — worth knowing which failure mode you're looking at when debugging.
typescript
9
What this never covers
Worth stating plainly, since this mechanism can look like it solves a problem it deliberately doesn't.
typescript
For wiring a resolution UI to the universal register described in Step 4, see offline/conflict-ui. For the offline queue mechanics conflicts build on top of, see core-concepts/offline-queue. For the full API signature of resolveUnifiedConflict(), see api/resolve-unified-conflict.