Conflict Resolution
Understanding how Yjs CRDTs automatically merge concurrent changes.
Core Principle
CRDTs guarantee eventual consistency: all clients receiving the same operations converge to identical state, regardless of operation order.
Y.Map: Last-Writer-Wins
When multiple clients set the same key, the highest logical timestamp wins. When timestamps are equal, the client ID breaks ties (arbitrary but consistent ordering):
Design tip: To preserve both values, use unique keys:
Y.Array: Position-Aware Merge
Concurrent insertions at the same position are both preserved:
Order of X/Y is deterministic (by client ID) but arbitrary. Design UIs that tolerate this.
Y.Text: Character-Level Merge
Different formatting attributes merge (bold + italic). Same attribute uses LWW (both set color → one wins).
Designing for Good Merges
Use ID-based references - Indices shift; IDs are stable.
Separate order from content - Content edits and reordering merge independently.
Avoid computed data - Don’t store totals; compute when needed.
Use granular keys - user.name, user.email instead of one user object.
Accept non-determinism - Concurrent inserts at same position have arbitrary order.
When Automatic Merge Isn’t Enough
For critical data, detect conflicts and show resolution UI:
Example: Counters and votes. Rather than storing a single number (where concurrent increments get lost to LWW), track each user’s contribution separately:
This pattern preserves all concurrent operations by giving each user their own key.
See Also
- ID-Based Storage - Stable references
- Transactions - Atomic operations