Skip to content
Published on

Realtime Collaboration & CRDT 2026 Deep Dive — Liveblocks, PartyKit, Yjs, Automerge, Loro, ShareDB, Replicache, Fluid, Tldraw sync

Authors

Prologue — What "Editing Together" Means in 2026

When Google Docs gave ordinary users simultaneous document editing in 2010, the underlying technology was OT (Operational Transform): transforming one person's edit operation against another's. It worked, but it was server-centric, offline was hard, and a buggy transformation function would haunt you forever.

By May 2026, the landscape has flipped. CRDTs (Conflict-free Replicated Data Types) are now the default unit of web collaboration. Figma's multiplayer infrastructure, Linear's local-first sync, Notion's live cursors, tldraw's realtime canvas, Excalidraw's collaboration rooms — all of these stand on CRDTs or their cousins.

Two larger shifts compound this.

  • November 2025: Vercel folded Liveblocks and PartyKit into close partnerships within months of each other, cementing a "Vercel for Realtime" positioning.
  • The Ink & Switch lab's "Local-first software" manifesto is no longer academic curiosity. Automerge 2.x, Loro, and Yrs (the Rust port of Yjs) have shipped in production.

This essay walks the 2026 realtime stack in one breath. Theory (CRDT vs OT, vector clocks, Lamport), library comparisons (Yjs vs Automerge vs Loro), SaaS comparisons (Liveblocks vs PartyKit vs Hocuspocus), and operational patterns (WebSocket vs WebRTC, awareness, persistence).


Chapter 1 · The Essence of Simultaneous Editing — Same Document, Different Timelines

First, let us define the problem. Two people editing the same document at the same time means, in computer-science terms, two independent operations applied to the same state.

t0:    "Hello"           (server = client A = client B)

t1:    client A inserts ", world" at position 5
t1:    client B inserts "Say: " at position 0
       (neither knows about the other)

t2:    both operations meet at the server
       -> what should the result be?

There are two answers. OT "transforms each operation to fit the other's coordinate system." CRDTs "assign absolute, unique IDs from the start so the operations can be applied as-is."

OT assumes a single source of truth (the server), but CRDTs let every client converge to the same state with or without a server (called eventual consistency). That property is the foundation of local-first software.


Chapter 2 · CmRDT vs CvRDT — Operations or State

CRDTs split into two families.

  • CmRDT (Operation-based): propagate operations. "Insert 'x' at position 5" is sent. Messages are small but require exactly-once delivery or causal ordering.
  • CvRDT (State-based): propagate the whole state or chunks of it. A merge function combines two states at sync time. The merge must be commutative, associative, and idempotent.

Yjs and Automerge both started op-based, but the 2026 generation — Loro, Y-CRDT, Automerge 2.x — has evolved toward delta-state or hybrid models: send only the diff, but in a form that can be applied idempotently.

FamilyMessage SizeNetwork GuaranteesRepresentative Library
CmRDTSmallCausal order + at-least-onceEarly Treedoc, RGA
CvRDTLargeBest-effort, idempotentEarly Riak DT
Delta-stateMediumIdempotent + causal metadataYjs, Automerge 2.x, Loro

Chapter 3 · OT vs CRDT — Why CRDTs Won

CriterionOTCRDT
Central server requiredEffectively yesNo (P2P possible)
Offline editsHardNatural
Verifying transform functionsNotoriously hard (TP2 problem)Not needed
Memory usageSmallLarger (metadata accrual)
Maturity for textBattle-tested in 2000sBattle-tested in 2020s
Headline usersGoogle Docs (historical)Figma, Linear, Notion live cursors

Google Docs still uses OT (legacy plus a verified infrastructure). But practically every new collaborative app since the 2020s has chosen CRDTs. The biggest reasons are offline support and P2P capability.


Chapter 4 · Vector Clocks and Lamport Timestamps — Two Faces of Distributed Time

Distributed systems have no absolute clock, so we use two tools.

  • Lamport timestamp: every event gets a monotonically increasing integer. To compare two nodes, take max + 1. Total order is possible, but you cannot tell whether two events are concurrent.
  • Vector clock: carry per-node counters. [A:3, B:2, C:1]. Comparing two vectors yields exactly one of "happens-before / happens-after / concurrent."

Yjs assigns each client a unique ID and combines client ID + monotonic sequence to identify operations (essentially Lamport plus client ID). Automerge uses actor ID + sequence number (a vector-clock variant). Loro follows the actor + counter model too.


Chapter 5 · Text CRDT Theory — Inside Yjs's Y.Text

Text is the hardest CRDT. Index-based operations like "insert 'x' at position 5" break under concurrent editing (someone inserts at 4 and your "5" is now "6"). So CRDTs use unique-ID-based positioning.

Yjs's Y.Text assigns every character a (clientID, clock) pair and threads them as a double-linked list of Items. An insert becomes "insert after this ID," and a delete leaves a tombstone. Concurrent inserts at the same position are ordered deterministically by clientID.

// Yjs: two people edit the same document
import * as Y from 'yjs'

// Client A
const docA = new Y.Doc()
const textA = docA.getText('content')
textA.insert(0, 'Hello')

// Client B (initially offline)
const docB = new Y.Doc()
const textB = docB.getText('content')
Y.applyUpdate(docB, Y.encodeStateAsUpdate(docA))
textB.insert(5, ', world')

// Meanwhile, client A also edits
textA.insert(0, 'Say: ')

// Exchange updates both ways
Y.applyUpdate(docA, Y.encodeStateAsUpdate(docB))
Y.applyUpdate(docB, Y.encodeStateAsUpdate(docA))

// Both documents converge to the same state
console.log(textA.toString()) // "Say: Hello, world"
console.log(textB.toString()) // "Say: Hello, world"

The crux is encodeStateAsUpdate and applyUpdate. In any order, any number of times, over any path, the result is the same. Idempotent, commutative, associative.


Chapter 6 · Yjs Internals — The Item Linked List and GC

The heart of Yjs is the Item. Each Item has:

  • id: { client: number, clock: number } — unique identifier
  • origin: ID | null — "which Item is this inserted after"
  • rightOrigin: ID | null — used to resolve concurrent-insert conflicts
  • content: ContentString | ContentEmbed | ... — the actual value
  • deleted: boolean — tombstone flag

To keep memory in check, Yjs performs GC. Deletions seen by all clients are removed from the tombstone, and adjacent Items from the same client get compacted into blocks (e.g., the five Items typed when you wrote "Hello" become one).

Benchmarks consistently rank Yjs among the fastest text CRDTs. Million-character documents remain practical in memory and CPU.


Chapter 7 · Automerge — The JSON CRDT Standard

Automerge began around the same time as Yjs but focuses on treating whole JSON documents as a CRDT. It supports text (Automerge.Text, recently a splice-based API), but its strength is merging deeply nested object trees.

// Automerge: two people edit the same JSON document
import { from, change, save, load, merge } from '@automerge/automerge'

// Initial document
let doc1 = from({
  title: 'Realtime collab notes',
  todos: [{ done: false, text: 'Write CRDT post' }],
})

// Serialize and send to another client
const binary = save(doc1)
let doc2 = load(binary)

// Concurrent edits on both sides
doc1 = change(doc1, (d) => {
  d.todos.push({ done: false, text: 'Add references' })
  d.title = 'Realtime collab notes (draft)'
})

doc2 = change(doc2, (d) => {
  d.todos[0].done = true
})

// Merge both ways
const merged1 = merge(doc1, doc2)
const merged2 = merge(doc2, doc1)

// Both merges produce identical results
console.log(JSON.stringify(merged1) === JSON.stringify(merged2)) // true

Automerge 2.x was rewritten in Rust (automerge-rs) and compiled to WASM, running in browsers, Node, and Electron alike. Memory and speed improved by an order of magnitude over 1.x.


Chapter 8 · Loro — The 2024-2026 Dark Horse

Loro is a newer CRDT library out of China that hit 1.0 in 2024 and, by 2026, sits alongside Yjs and Automerge as one of the "big three." Highlights:

  • Rust + WASM from day one
  • Time travel as a first-class feature — rewind the document to any historical point
  • Rich-text mark CRDTs (style ranges) handled with precision in dedicated structures
  • Tree CRDTs (concurrent moves under parent-child relationships) treated seriously

Benchmarks show Loro outperforming Yjs on certain workloads (especially trees and rich text). Yjs still has the edge in plain text editing, ecosystem maturity, and learning resources.


Chapter 9 · Liveblocks — The First CRDT-as-a-Service Champion

Liveblocks (Paris-based) was first to nail down the "multiplayer infrastructure as SaaS" proposition. As of 2026:

  • LiveObject, LiveList, LiveMap — their own CRDTs (separate from Yjs)
  • useStorage / useMutation — React hooks for shared state
  • Awareness/Presence — realtime cursors, names, selections
  • Comments / Threads — a separate module for commenting on documents
  • Yjs adapter — existing Yjs apps (Tiptap, BlockNote, Lexical's Yjs adapter) can sync over Liveblocks transport
// Liveblocks: read and write shared state via the useStorage hook
import { useMutation, useStorage } from '@liveblocks/react/suspense'

function Todos() {
  const todos = useStorage((root) => root.todos)

  const addTodo = useMutation(({ storage }, text: string) => {
    const list = storage.get('todos')
    list.push({ id: crypto.randomUUID(), text, done: false })
  }, [])

  const toggle = useMutation(({ storage }, id: string) => {
    const list = storage.get('todos')
    const idx = list.findIndex((t) => t.id === id)
    if (idx >= 0) {
      const item = list.get(idx)
      list.set(idx, { ...item, done: !item.done })
    }
  }, [])

  return (
    <ul>
      {todos.map((t) => (
        <li key={t.id} onClick={() => toggle(t.id)}>
          {t.done ? 'X' : 'O'} {t.text}
        </li>
      ))}
    </ul>
  )
}

Liveblocks's strengths are operational simplicity and React-optimized DX. The weaknesses are pricing (seat-based and MAU-based) and lock-in to their proprietary CRDT (self-hosting fully is hard).


Chapter 10 · PartyKit — Collaboration on Cloudflare Durable Objects

PartyKit (founded by Sunil Pai in 2023, acquired by Cloudflare in 2024) takes a different route. Each room is a Durable Object that runs arbitrary code — Yjs, Automerge, or your own logic.

// PartyKit: server code (server.ts)
import type * as Party from 'partykit/server'
import { onConnect } from 'y-partykit'

export default class YjsServer implements Party.Server {
  constructor(readonly room: Party.Room) {}

  async onConnect(conn: Party.Connection) {
    // y-partykit handles Yjs sync automatically
    return onConnect(conn, this.room, {
      persist: { mode: 'snapshot' },
    })
  }
}

PartyKit's advantage is running user code at the edge. Auth, permissions, persistence, and game logic can all be written per room. The downside is Cloudflare lock-in — although by 2026 that is arguably a benefit, since global edge distribution comes for free.


Chapter 11 · Self-Hosting — Hocuspocus + Yjs

If cost or lock-in is a concern, Hocuspocus is the answer. Built by the Tiptap team, it is a Yjs backend that runs on Node.js and persists to Postgres, SQLite, or Redis.

// Hocuspocus server
import { Server } from '@hocuspocus/server'
import { Database } from '@hocuspocus/extension-database'

const server = new Server({
  port: 1234,
  extensions: [
    new Database({
      fetch: async ({ documentName }) => {
        // Load Yjs binary from DB
        const row = await db.documents.findUnique({ where: { name: documentName } })
        return row?.state ?? null
      },
      store: async ({ documentName, state }) => {
        // Save to DB
        await db.documents.upsert({
          where: { name: documentName },
          update: { state, updatedAt: new Date() },
          create: { name: documentName, state },
        })
      },
    }),
  ],
  async onAuthenticate({ token, documentName }) {
    // JWT verification, etc.
    if (!verifyToken(token)) throw new Error('Unauthorized')
    return { userId: extractUserId(token) }
  },
})

server.listen()

You own the operations, and data sovereignty is yours. The trade-off: scaling, HA, and monitoring fall on you. Reach a Notion or Linear level and you will end up building custom infrastructure anyway.


Chapter 12 · ShareDB — Survivor of the OT Camp

ShareDB (formerly derby-share) is an OT-based self-hosted library. Widely used in pre-Yjs collaboration apps (around 2015) and still in active service in teams comfortable with OT.

  • Represents documents as JSON and propagates JSON OT operations
  • Persists to MongoDB or Postgres
  • Text OT (json0 and json1 types) is battle-tested
  • Weakness: P2P and offline are less natural than with CRDTs

For new 2026 projects, the standard recommendation is CRDTs (Yjs/Automerge/Loro). But a healthy ShareDB system is not worth ripping out.


Chapter 13 · Replicache and Zero — A Different Answer to "Local-First Sync Engines"

Rocicorp's Replicache (2020-) and its successor Zero (2025-) are not CRDTs. They use a mutator-based sync + authoritative server + optimistic UI model.

  • The client defines "mutators" (e.g., addTodo(text)).
  • The mutator runs locally and the UI updates immediately.
  • The same mutator request is sent to the server, where it is re-run authoritatively.
  • When the server result arrives, the local result is overwritten (rollback possible).

If CRDTs say "multiple truths eventually converge," Replicache says "the server is the final truth and the client makes an optimistic guess." Linear made this model famous.


Chapter 14 · Fluid Framework — Microsoft's Enterprise Answer

Fluid Framework powers Microsoft 365's realtime collaboration (Office Online, Loop). It is not strictly a CRDT but a sequence-based sync model of its own.

  • Distributed Data Structures (DDS) like SharedString, SharedMap, and SharedTree
  • Runs on Azure Fluid Relay
  • An official SDK exists but external adoption is rare; effectively a Microsoft-internal engine

A new external app in 2026 is unlikely to pick Fluid, but if you build collaboration on top of Office or Microsoft Graph, it is worth knowing.


Chapter 15 · Whiteboard Collaboration — Tldraw and Excalidraw

tldraw (@steveruizok, @orta) is a whiteboard and diagram library, and they built their own multiplayer sync engine. Internally it resembles op-based Yjs, but it is optimized for the canvas data structure (a shape tree). The @tldraw/sync package syncs rooms via React + a WebSocket server, the reference implementation runs on Cloudflare Durable Objects, and per-shape Last-Writer-Wins (LWW) matches the semantics of drawing tools.

Excalidraw, by contrast, is the icon of hand-drawn whiteboards, and its collaboration rooms run on an encrypted Firebase Realtime Database. It does not use a formal CRDT, just LWW with client-side merges. Surprisingly reasonable: whiteboards are short sessions with a small number of concurrently edited shapes, and CRDT metadata overhead is not needed.

tldraw's lesson: CRDTs are designed differently for different data structures. Text CRDTs (Yjs Y.Text) and canvas CRDTs (tldraw) share a theoretical foundation but their implementation details differ entirely. Not every collaborative app needs a full CRDT.


Chapter 16 · Figma's Multiplayer Architecture

Figma built its own multiplayer engine in 2016-2017. It is a CRDT variant applying LWW per node and per property. The essentials:

  • Document = tree of nodes; each node = a property bag
  • Each property carries a (timestamp, clientID) of its latest change
  • Conflict = larger timestamp wins (LWW); ties broken by larger clientID
  • Tree structure changes (re-parenting, reordering) use a separate tree CRDT layer

Figma engineer Evan Wallace published this architecture in a 2019 blog post, and many collaboration apps adopted similar patterns. tldraw, in part, drew inspiration from it.


Chapter 17 · Google Docs and the History of OT

Google Docs began with the 2010 Writely acquisition and launched simultaneous editing later that year. The engine is Jupiter (Google's internal name) OT. Core ideas:

  • Every edit is a (revision, operation) pair
  • Clients send op to the server; the server returns transformed ops
  • TP1 (Transformation Property 1): op1 \* (op2 transformed against op1) == op2 \* (op1 transformed against op2) must hold

Writing OT transform functions correctly is notoriously hard. Google polished its code over more than a decade. The industry concluded the cost was too high for new apps and moved to CRDTs.


Chapter 18 · Presence / Awareness — "Who Is Looking at What, and Where?"

The other half of collaboration is presence. Who is online, where their cursor is, what they have selected, what color they appear as.

Yjs standardized this with y-protocols/awareness. It is an ephemeral state distinct from the CRDT body. Metadata includes:

  • clientID (shared with the Y.Doc)
  • name, color, cursor, selection, and free-form JSON
  • Heartbeats to indicate liveness; automatic cleanup on disconnect

Liveblocks treats awareness as a first-class citizen and exposes useOthers() and useUpdateMyPresence(). PartyKit can implement it with its own broadcast primitives. Either way, presence should not be persisted — when the room closes, it goes away.


Chapter 19 · WebSocket vs WebRTC — Choosing the Transport

CRDT sync goes over one of two transports.

CriterionWebSocketWebRTC (DataChannel)
Server requiredRequiredOnly STUN/TURN for NAT traversal
LatencyServer round-tripDirect P2P (can be lower)
ScalingServer fans outMesh topology is N^2
PersistenceNaturalNeeds server mirror
FirewallUsually passesMay be blocked without TURN
Standard librariesy-websocket, y-partykity-webrtc

The 2026 default recommendation: WebSocket as the primary, with WebRTC as a supplement when true P2P matters (security, offline mesh) or when you want to offload server traffic.


Chapter 20 · CRDT Library Comparison

LibraryLanguageStrengthsWeaknessesHeadline Users
YjsJS/TS (+Yrs Rust port)Maturity, ecosystem, text performanceMemory footprint a bit largeTiptap, BlockNote, Lexical Yjs
AutomergeRust/WASMStrong JSON tree mergingText performance below YjsPushPin, Trail Runner
LoroRust/WASMRich text, trees, time travelNewer ecosystemNew apps
Yjs + YrsRust portNative Rust backendWire compatibility with Yjs only partialServer-side Yjs
Diamond TypesRustBenchmark champion for single-doc textNo JSONBenchmarks
sjs (Synchronized JS)ExperimentalTypeScript-friendlyNewResearch

Chapter 21 · SaaS vs Self-Hosting Comparison

CriterionLiveblocksPartyKitHocuspocus (self)ReplicacheFluid (MS)
CRDT familyProprietary + Yjs adapterYjs/Automerge, freelyYjsNon-CRDT (optimistic)DDS (non-CRDT)
HostingSaaSCloudflare edgeSelfSelf or RocicorpAzure
PricingMAU-basedUsage-basedInfra onlyUsage + SaaSAzure rates
Self-host possiblePartial (Edge Storage option)Hard (CF tied)FullyReplicache: yesHard
Data sovereigntyUS / EU optionsGlobal edgeFree choiceFree choiceAzure regions
Awareness first-classYesDIYYesN/APartial
Comments / Threads moduleYesNoneNoneNoneLoop separate

Chapter 22 · Korea and Japan Cases — Notion, Kakao, Cybozu, Sansan

Korea's collaboration market is interesting. After Notion's Korean launch (2020), the share of Notion API plus self-built collaboration rose quickly (Notion uses a proprietary CRDT-like model, with little public documentation). Kakao Work and Kakao Talk messages are not CRDTs, but Kakao Work's boards and memo collaboration use OT (publicly stated), and Kakao Enterprise built its own engine during its "remote collaboration" push. Naver Line Works covers document, table, and calendar collaboration; integrated with Line in the Japanese market, it holds significant Japanese enterprise share. Toss / Toss Payments has publicly discussed using Yjs + Tiptap for internal collaboration tools (archives, documents).

Japan is interesting too. Cybozu kintone, a business-app builder, applies OT-based transforms when multiple users edit the same record concurrently; Cybozu is famous for its long OT expertise. Sansan, a business-card cloud, has presented CRDT applications in workflows where multiple users correct OCR results of card data concurrently. Rakuten RMS is the shop-owner collaboration tool for Rakuten malls, with its own internal sync engine. Notion's Japan team runs the same engine as English-speaking markets, but a Japanese PM presentation revealed patches for collaboration conflicts with Japanese IME (in-progress composed characters).


Chapter 23 · CRDT Adoption Decision Matrix

When are CRDTs the answer, and when are they not?

SituationRecommendation
Concurrent text editing (an editor)Yjs + Tiptap/BlockNote/Lexical-Yjs
Whiteboard / canvastldraw sync or Yjs Y.Map
Forms / tables (per cell)Yjs Y.Map with per-cell keys or ShareDB
Game state (time-deterministic)Lock-step or authoritative server, not CRDTs
Chat messagesNot needed, append-only log
Notifications / realtime countersCRDT G-Counter (academically), or Redis
Offline-first (local-first)Automerge, Loro, Yjs IndexedDB persistence
Server authority matters (payments, inventory)Replicache or classic transactions

Chapter 24 · Persistence Strategy — IndexedDB, Postgres, Binary Snapshots

CRDTs need to be stored somewhere.

  • Client-side: Yjs's y-indexeddb, Automerge's IndexedDB adapter. After offline edits, sync resumes automatically on reconnect.
  • Server-side (Yjs binary): Yjs's encodeStateAsUpdate produces a full-state binary. Postgres bytea columns, S3 objects, Redis — anywhere works.
  • Server-side (delta log): accumulate every update in a log and periodically compact to a snapshot. Useful for large documents.
  • Hybrid: snapshot + post-snapshot deltas. On load, apply snapshot first and then deltas.

The hidden trap is GC. Tombstones seen by every client can be deleted, but knowing what "every client has seen" is hard. Yjs is conservative and lets operators run explicit compaction periodically.


Chapter 25 · Security, Permissions, End-to-End Encryption

Security models for collaboration SaaS fall into three categories.

  • Server sees plaintext (most common): the standard Liveblocks and Hocuspocus model. Permission checks, search, and comment moderation are easy.
  • End-to-end encryption (E2EE): clients encrypt Yjs updates before sending. The server only sees binary blobs and knows no semantics. Excalidraw rooms use a similar model. Weakness: search, server-side comment analysis, and server-side merging are impossible.
  • Partial encryption: content is E2EE, metadata (room membership, presence) is plaintext. A middle ground.

Permissions typically combine per-room tokens with per-operation checks: who can enter (JWT) and what they can do once inside (onAuthenticate + beforeHandleMessage in Hocuspocus).


Chapter 26 · Epilogue — Looking at 2027

CRDTs have left the academic realm and entered operations. Three changes to expect by 2027:

  1. Mobile and native become first-class citizens. Yjs Swift ports, Automerge Kotlin, and Loro Flutter bindings stabilize, and mobile collaboration apps explode. Local-first becomes genuinely viable.
  2. AI agents become collaboration participants. Cursor and Claude Code become another "user" editing the same document in real time, and awareness distinguishes humans from AI.
  3. Standardization begins. CRDT wire formats enter discussion at IETF / W3C levels. Yjs, Automerge, and Loro are not yet wire-compatible, but the contour of a "standard text CRDT" is starting to appear.

The biggest lesson is simple. The world without a Save button has already arrived. What is left is deciding what to build on top.


References