// FULL-STACK
Collaborative Editor with CRDTs
Real-time collaborative editing via a custom RGA Sequence CRDT
Overview
Google Docs makes real-time collaboration look effortless. Multiple cursors, simultaneous edits, no conflicts. Under that simplicity is one of the hardest problems in distributed systems: how do you let multiple users edit the same document concurrently, without a central lock or coordination server?
The traditional answer is Operational Transformation (OT) — what Google Docs actually uses. But OT requires a central server to serialize operations, and correctness proofs are notoriously tricky. The modern alternative is CRDTs (Conflict-free Replicated Data Types) — data structures that are mathematically guaranteed to converge, regardless of the order operations arrive in.
I built this collaborative editor with CRDTs from scratch — no external CRDT libraries, no OT — to deeply understand how it works.
Core ideas
- RGA Sequence CRDT implemented from first principles — every character has a unique ID (
(siteId, counter)), a value, and a tombstone flag for deletes - Out-of-order op handling — incoming inserts are buffered if their predecessor hasn't arrived yet; ghost-deletes are cached for ops referencing characters we haven't seen
- Eventual consistency — every replica converges to the same document state regardless of network delays or partition order
- Live remote presence — cursors of other users are tracked and rendered with their names in real-time
Architecture
- Frontend — React + Vite, with Monaco Editor wrapped to bind into the CRDT layer (Monaco's programmatic-edit API is rich enough that we can patch in/out CRDT ops without fighting the editor)
- Transport — raw
wsWebSockets for low-latency bidirectional sync - Backend — Node.js + Express; a lightweight relay that broadcasts ops to all clients in the room
- Storage — SQLite for rooms + documents, so sessions resume after disconnect
- Shared types — TypeScript monorepo with
crdt-core(pure CRDT logic) shared between client and server, so the same convergence guarantees apply everywhere
┌─ Frontend (React) ──────────────────────────────┐
│ Monaco Editor ◄► CRDT Client ◄► WebSocket │
└──────────────────────────────────────────────────┘
│ ws://
┌─ Backend (Node.js) ──────────┼───────────────────┐
│ SQLite store ◄► CRDT merge engine ◄► WS │
└──────────────────────────────────────────────────┘
Why CRDTs (not OT)
- No central authority — every replica can commit locally and broadcast; convergence happens by construction
- Easier reasoning — RGA's correctness comes from total ordering of IDs, not from running an OT transformation function over every concurrent edit pair
- Offline-friendly — local edits commit immediately; sync happens whenever the connection returns
- Worse memory profile than OT — tombstones never get garbage-collected naively. Trade-off worth knowing about.
Why build it
The CRDT literature is famously dense. Implementing RGA from scratch — handling the buffering, the tombstones, the ghost-deletes, the merge engine — is the only way to actually internalize why the algorithm works. Plus, you end up with a useful tool.