C.W.K.
Stream
Lesson 02 of 08 · published

Collaborative Editing

~13 min · app, collab, ot, crdt

Level 0Poller
0 XP0/60 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

The challenge: concurrent edits

Two users typing in the same document at the same time will conflict. Naive "last-write-wins" loses data. The two production-grade approaches are Operational Transforms (OT) and Conflict-free Replicated Data Types (CRDTs).

OT vs CRDT in one paragraph

OT (Google Docs) requires a central server. Each edit is an operation with an integer revision; the server transforms incoming operations against any concurrent ones to produce a consistent result. CRDT (Figma, Notion) requires no central authority — each character has a globally unique ID and ordering metadata, and the data structure mathematically converges no matter the operation order. CRDTs are easier to reason about; OT is more bandwidth-efficient.

Use a library

Implementing either correctly is months of work and the wrong place to be original. Yjs (CRDT) is the dominant JS library; Automerge is its main competitor. Both have WebSocket transport adapters. Wire them in; spend your time on UX, not algorithms.

Code

Conceptual: a CRDT-shaped insert·javascript
// Each character gets a globally unique id.
// Order is determined by ids, not local indices.
ws.send(JSON.stringify({
  type: 'doc.insert',
  data: {
    id: 'user1-1705312000-0',  // globally unique
    char: 'H',
    after: null,                // position reference
  },
}));

// On the receiving side, all operations from all users
// merge into the same final state regardless of order.
Yjs over WebSocket — the production-ready path·javascript
import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';

const ydoc = new Y.Doc();
const provider = new WebsocketProvider(
  'wss://api.example.com/yjs',
  'document-id-here',
  ydoc,
);

const ytext = ydoc.getText('content');
ytext.observe(event => {
  // event.changes — apply to your editor view
});

// Local edit
ytext.insert(0, 'Hello');

External links

Exercise

Wire Yjs to two browser tabs against a shared room. Type in one; watch it appear in the other. Disconnect tab A, edit both; reconnect; confirm that both edits merge correctly without loss. Try to break it — concurrent inserts at the same position, deletions over inserts. The library should handle every case.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.