What is a CRDT? Replicated Data Types Explained
A CRDT (conflict-free replicated data type) is a data structure where multiple copies can be edited independently and always merged into a consistent result, without a central server coordinating changes. If two people edit the same document on different devices, even offline, CRDTs guarantee that both edits are preserved and the documents converge to the same state when they reconnect.
// A CRDT's merge must be:
// commutative — order doesn't matter
// associative — grouping doesn't matter
// idempotent — applying twice changes nothing
trait Crdt {
fn merge(&mut self, other: &Self);
}A type is a CRDT if its merge satisfies all three.
How CRDTs work
With a CRDT, every user holds a full copy of the data. Edits happen locally, with no round-trip to a server. When devices reconnect, each copy shares its changes, and a merge function folds them into an identical result on every device. The ordering, deduplication, and convergence all come from the data structure itself.
Most collaborative editors today still route every message through a centralized server. The server decides the final order of operations and relays the result to every connected client. That centralization isn't a stylistic choice, it's a structural requirement of older approaches. Without a single authority, two clients can submit "insert 'X' at position 5" at the same moment and disagree forever about whose edit landed first. Operational Transformation and its descendants lean on the server to be the one arbiter of truth.
CRDTs are opening up the other half of the design space. Ordering, deduplication, and conflict resolution all move off the server and into the data itself. A device can accept edits while offline, ship them to peers later, and still arrive at the same final state as every other device, without anyone asking "who is right." Any central server in the picture becomes a convenience for discovery and backup, not the source of truth.
CRDTs encode those convergence guarantees directly into the data structure itself. The merge function that combines two documents has three mathematical properties.
- Commutative means order doesn't matter. Replica A merging with replica B produces the same result as B merging with A.
- Associative means grouping doesn't matter. Merging three replicas in any combination of pairs reaches the same final state.
- Idempotent means applying the same change twice has no additional effect. Duplicate messages during sync are harmless.
These properties together guarantee that every replica converges to the same result regardless of network delays, message reordering, or retransmission. Modern CRDT libraries like Automerge and Loro build on these principles and add optimizations for specific data types like rich text, lists, trees, and maps.
CRDT explained: a worked example in Rust
The simplest useful CRDT is a grow-only counter. Imagine you build a digital birthday card in CoCube, send the link to twenty friends, and each one can sign it from whatever device they have handy. The top of the card shows "14 friends have signed!"
The naive version is a single integer that every device increments.
struct Counter(u64);
impl Counter {
fn sign(&mut self) {
self.0 += 1;
}
}Now picture two devices spending the afternoon offline, with different friends signing from each. Device A ends up with Counter(7). Device B ends up with Counter(10). When they reconnect, how do they merge? Taking the maximum gives 10 and loses seven signatures. Taking the sum gives 17, which is right only if the two devices never synced together before. If they had synced once earlier and then drifted apart, summing would double-count every signature from before that earlier sync. A single integer carries no history. It can't tell the two scenarios apart.
A grow-only counter fixes this by splitting the tally into per-device slots. Each device only writes to its own slot, and the top-line total is the sum of every slot. Merging two counters takes the per-slot maximum, which always works because each slot only grows.
use std::collections::HashMap;
struct GCounter {
counts: HashMap<String, u64>,
}
impl GCounter {
fn sign(&mut self, device: &str) {
*self.counts.entry(device.to_string()).or_insert(0) += 1;
}
fn total(&self) -> u64 {
self.counts.values().sum()
}
}
impl Crdt for GCounter {
fn merge(&mut self, other: &Self) {
for (device, &count) in &other.counts {
let slot = self.counts.entry(device.clone()).or_insert(0);
*slot = (*slot).max(count);
}
}
}The three merge properties fall out of per-slot max. Swapping which device merges first produces the same map, making merge commutative. Chaining merges in any grouping produces the same map, making merge associative. Merging a device with itself is a no-op, making merge idempotent.
Libraries like Loro and Automerge define a large set of CRDTs you can use to model real problems. They cover text, lists, maps, and trees, with binary export and import for syncing between peers.
Why CRDTs matter
The shift toward local-first software, where your data lives on your device instead of on someone else's server, is bringing CRDTs out of specialized infrastructure and into the software people use every day.
Three properties set them apart.
- Offline editing. Make changes without an internet connection and sync when you reconnect.
- No single point of failure. The system keeps working without a central server being online.
- Guaranteed consistency. Every replica converges to the same state by mathematical construction. No "your changes were overwritten" dialogs.
For anyone building tools where multiple people or multiple devices edit the same data, CRDTs replace fragile strategies like document locking or last-writer-wins with time-independent guarantees. Locks break down the moment a collaborator goes offline holding one. Last-writer-wins silently discards concurrent edits the instant two devices' clocks disagree.
CRDTs in practice
Not every collaborative system uses CRDTs. Figma deliberately doesn't, since its central server can order per-property writes directly without the overhead of CRDT metadata. For systems that accept decentralization or offline-first editing, CRDTs are what ship. Production uses fall into three broad categories.
- Real-time collaborative tools. Zed's collaborative code editing runs on a custom Rust CRDT derived from RGA and causal-tree research. JupyterLab's real-time layer syncs notebook cells through Yjs. AFFiNE runs on y-octo, its own Rust Yjs-compatible engine.
- Local-first apps. Actual Budget uses a hand-rolled CRDT over SQLite with hybrid logical clocks to sync personal finance across devices. Anytype uses its own any-sync protocol for peer-to-peer, end-to-end-encrypted knowledge graphs. CoCube uses the Loro CRDT library to store every document as a live CRDT data structure, and edits sync between devices through binary CRDT snapshots and incremental operations, whether you're online or off.
- Databases and sync engines. Redis Active-Active uses custom CRDTs to resolve conflicts across geo-distributed replicas. AntidoteDB is a research-grade database built entirely on CRDTs. cr-sqlite turns ordinary SQLite tables into CRDTs with a single extension.
CRDT vs OT: how they differ
Operational Transformation (OT) is the older collaboration model, and it still powers Google Docs and most server-mediated editors today. OT requires a central server to transform and reorder operations. CRDTs don't.
| CRDTs | OT | |
|---|---|---|
| Central server required | No | Yes |
| Offline editing | Built-in | Limited |
| Complexity | In the data structure | In the transform functions |
| Proven at scale | Growing (Zed, Redis) | Mature (Google Docs) |
OT is proven at scale in server-centric architectures. CRDTs are the better fit when a central server becomes a limitation instead of a feature, whether in peer-to-peer systems, offline-capable apps, or local-first tools that put the user's device first.
Modern CRDT libraries
Several open-source CRDT libraries ship in production today.
| Library | Language | Data types |
|---|---|---|
| Yjs | JavaScript | Text, arrays, maps, XML |
| Automerge | Rust + JS/WASM | JSON-shaped documents with rich text support |
| Loro | Rust + JS/WASM | Rich text, lists, maps, movable trees |
| Diamond Types | Rust | Plain text |
Yjs has the largest ecosystem and the widest range of editor integrations. Automerge's JSON data model fits application state that goes beyond text. Loro optimizes for rich text and movable trees, which is why CoCube uses it. Diamond Types by Joseph Gentle is a high-performance text CRDT, widely cited for its benchmark results on plain text.
These libraries all trace back to the 2011 paper Conflict-free Replicated Data Types by Marc Shapiro, Nuno Preguiça, Carlos Baquero, and Marek Zawirski, which formalized the guarantees the field now takes for granted.
Where CRDTs fall down
CRDTs solve merge. They don't solve everything a centralized server quietly handled for you.
Schema migrations. A central server can migrate every row in a table at once and guarantee that the next read sees the new shape. With CRDTs, replicas update on their own timeline, and old and new schema versions coexist for long stretches. Document shapes need to be forward-compatible, and migration code needs to handle both versions running in parallel.
Authentication and access control. CRDTs don't encode who is allowed to write what. Any replica that holds the bytes can modify them. Enforcing "user A can edit this section, user B cannot" requires either a trusted relay that inspects updates before fan-out, or signed operations that replicas verify on import. Ink & Switch's Keyhive project is an active effort to solve this at the data layer.
Sharing and discovery. Two devices can't merge if they can't find each other. Without a central index, telling peers "this document exists at that replica" requires an out-of-band channel, usually a discovery server. Pure peer-to-peer discovery is rarely practical for consumer apps.
Document size and sync cost. CRDTs carry metadata alongside the content so merges can be deterministic, and that metadata doesn't shrink as the document lives longer. A fresh device importing a five-year-old document pays for every edit that ever happened. Mature libraries invest heavily in compaction and encoding to keep this cost bearable, as Kevin Jahns details in the Yjs performance post.
Most local-first apps, including CoCube, run a lightweight server for auth, discovery, and backup even though the data itself is a CRDT. The server is a convenience, not the source of truth.
Related terms
- Local-first software describes applications that store data on the user's device and sync in the background, often using CRDTs for conflict resolution.
- Eventual consistency is a consistency model where all replicas converge to the same state given enough time. CRDTs guarantee this by construction.
- Causal consistency is a stricter consistency model where operations that depend on each other appear in the same order on every replica. Many CRDTs provide this alongside eventual consistency.
- Operational Transformation (OT) is an alternative approach to collaborative editing that relies on a central server to order and transform operations.
- Peer-to-peer describes a network topology where nodes communicate directly without a central coordinator. CRDTs make peer-to-peer collaboration practical by removing the need for global ordering.
- Personal knowledge management is the broader discipline of organizing what you know, and the pillar that CRDT-based local-first tools live under.
- Reactive programming is how CoCube's cells recompute when CRDT state changes.
- Second brain is the popular framing for a personal knowledge system built on tools like CoCube.