I am a...
Learn more
How it worksPricingFAQ
Account
May 24, 2026 · 10 min read · By Japish Thind

How to build a real-time collaborative editor

build real time collaborative editor — How to build a real-time collaborative editor
Photo by [Christina Morillo](https://www.pexels.com/@divinetechygirl) on [Pexels](https://www.pexels.com/photo/person-using-silver-macbook-pro-1181467/)

How to build a real-time collaborative editor

To build a real-time collaborative editor in 2026, pick a CRDT library (Yjs or Automerge) for conflict-free sync, pair it with a rich-text framework (Tiptap or ProseMirror), and pipe updates through a WebSocket server (Hocuspocus, or a hosted layer like Liveblocks). Add presence, cursors, and comments on top. Skip Operational Transform unless you are forking Google Docs.

Most teams underestimate two things: offline-first behavior, and the long tail of UX work (cursor jitter, selection sync, comment threads, undo across users). The library choice takes a day. The UX takes a quarter.

Why this matters in 2026

Collaborative editing stopped being a novelty around 2022. Figma, Notion, Linear, Pitch, and Coda made multiplayer feel like the default. Customers now expect a cursor with someone's name on it inside any tool that holds shared state.

The shift in 2026 is that you no longer have to build the sync layer yourself. Yjs hit production scale at companies like Notion and Linear. Liveblocks turned the whole stack into a hosted SDK. Convex and Supabase added realtime primitives that cover 80% of presence and broadcast use cases. The bar for "minimum viable multiplayer" dropped from six months to a weekend.

The flip side: the bar for good multiplayer went up. If your competitor ships smooth cursors and instant comments, your janky 200ms sync will lose deals.

CRDT vs OT vs hosted: the only decision that matters

Every real-time editor sits on one of three foundations. Pick the wrong one and you will rewrite the sync layer inside a year.

ApproachHow it worksBest forTrade-offs
CRDT (Yjs, Automerge)Each client holds a replica. Edits merge deterministically without a central server.Offline-first apps, peer-to-peer, anything Notion-shapedLarger document size, eventual consistency, harder to do schema migrations
OT (ShareDB, what Google Docs uses)Edits are transformed against concurrent edits by a central server.Linear edit history, server-authoritative apps, plain-text docsServer is single point of failure, custom transform functions per data type, painful to extend
Hosted (Liveblocks, Convex, Supabase Realtime)Someone else runs the sync server. You get an SDK.Teams that want multiplayer in two weeks, not two quartersVendor lock-in, monthly cost scales with MAU, less control over conflict resolution

The honest take: in 2026, CRDTs win for most new editors. They handle offline gracefully, they scale horizontally (any node can take a write), and the ecosystem around Yjs in particular is mature. OT is still the right choice if you need a strict server-authoritative history (legal docs, anything regulated), or if your edit operations don't fit cleanly into a CRDT data model.

Hosted services are the right choice when your team is small, your runway is short, and your differentiation is somewhere other than the sync engine.

The library landscape, with real numbers

Here is what the field looks like as of mid-2026.

Yjs (CRDT, MIT)

The default choice. Powers Notion's collaborative blocks, Linear's comment editor, Evernote's new editor, and dozens of YC-funded tools. Document size scales linearly with edit count, but the y-indexeddb provider gives you free offline storage and a y-protocols package handles awareness (presence, cursors) cleanly. Pair with y-websocket or Hocuspocus for the server.

The footgun: Yjs documents grow forever unless you snapshot. Plan for a daily compaction job on docs older than 30 days.

Automerge (CRDT, MIT)

Maintained by Ink & Switch. Stronger theoretical foundation than Yjs (Automerge 2.0 is a port of the original research that defined the JSON CRDT). Better Rust core, smaller bundle in some benchmarks. The trade-off: smaller ecosystem, fewer rich-text bindings out of the box. Good fit if you need local-first across platforms (it has solid Swift and Kotlin bindings).

Liveblocks (hosted CRDT, freemium)

Built on top of Yjs under the hood, but you never touch the protocol. You get presence, comments, threads, notifications, and storage as React hooks. Pricing in 2026 starts free up to 50 MAU, then ~$29/mo for a Starter tier, with $299/mo and up for production teams. The Pro tier covers most pre-Series-B SaaS.

Liveblocks is the right pick if you want to ship multiplayer this sprint, not this quarter. The lock-in is real but the SDK quality is excellent.

Convex (realtime backend, freemium)

Not a CRDT. Convex is a transactional database with realtime subscriptions baked in. Great for collaborative apps where the state is more "shared object" than "rich document" (Linear-style issues, Notion-style databases, kanban boards). For a true rich-text editor, you would still use Yjs on top of Convex's storage.

Supabase Realtime (Postgres listen/notify + WebSocket, open source)

Cheapest path if you already use Supabase. It gives you presence and broadcast channels for free, plus row-level subscriptions. Not a sync engine for rich text on its own, but a great transport layer if you want to ship your own CRDT updates over it.

ShareDB (OT, MIT)

The OT veteran. Still actively maintained. Used by the original Etherpad-lite community and a few enterprise tools. Pick it only if you have a hard requirement for server-authoritative edit history.

The recommended stack (and why)

For most teams starting fresh in 2026, the stack that ships fastest with the fewest regrets is:

  • Yjs for the CRDT layer
  • Tiptap (built on ProseMirror) for the rich text framework
  • Hocuspocus for the WebSocket server (open source, made by the Tiptap team, drop-in Yjs compatibility)
  • y-indexeddb for offline storage in the browser
  • y-protocols/awareness for presence and cursors

If you want zero-ops and you are pre-revenue, swap Hocuspocus for Liveblocks. Same Yjs underneath, but they run the server, handle scaling, and ship a polished comments and presence SDK. You can always migrate off later (the Yjs document format is identical).

For non-rich-text use cases (kanban boards, dashboards, multi-cursor design tools), reach for Convex or Liveblocks Storage instead. You will write less glue code.

Step-by-step: the parts you cannot skip

Once the stack is chosen, the actual work breaks into six tracks. Each one has its own quarter-of-pain if you ignore it.

1. Document model and schema

Decide what a "document" is in your app before you write a line of sync code. With Yjs, you have Y.Map, Y.Array, Y.Text, and Y.XmlFragment as your building blocks. Rich text uses Y.XmlFragment (Tiptap and ProseMirror plug straight in). Structured data uses Y.Map.

Mistake to avoid: storing serialized JSON in a Y.Text. You lose all merge benefits and end up with last-write-wins.

2. Transport and persistence

Run a WebSocket server (Hocuspocus is the easy answer). Persist documents to Postgres or Redis between connections. Hocuspocus has an onStoreDocument hook for this; debounce writes (every 2 to 5 seconds is typical) so you do not hammer your DB on every keystroke.

For offline support, y-indexeddb mirrors the document locally and syncs on reconnect automatically. This is the single biggest UX win and it costs you one import line.

3. Presence: cursors, selections, names

Yjs ships y-protocols/awareness for ephemeral state (cursor positions, selection ranges, user names, colors). Awareness state is not persisted, which is exactly what you want. Cursors should jitter-smooth on the client (a 100ms ease is enough) so they do not feel laggy.

Practical detail: cursor positions need to survive concurrent edits from other users. ProseMirror has helpers for this. Tiptap exposes them via the Collaboration and CollaborationCursor extensions.

4. Conflict resolution and merging

CRDTs handle the technical merge for you. They do not handle the semantic merge. If two users edit the same paragraph and one deletes it while the other adds a sentence, the deletion wins (the sentence is gone forever). Decide upfront whether your app needs explicit conflict UI (Google Docs-style suggestions) or whether last-write-wins is acceptable.

For most product tools, the latter is fine. For legal or financial docs, build a suggestion mode and require explicit accept/reject.

5. Comments, threads, and annotations

This is where teams underestimate the scope. Comments need to anchor to a position in the document that survives edits around it. ProseMirror has Decoration and Mark for this. Tiptap Pro ships a Comments extension. Liveblocks ships Comments and Threads as a first-class feature.

The hard part: an anchored comment whose source text gets deleted. Decide whether the comment "orphans" (still shows but flagged as outdated) or disappears. Most users prefer orphan-with-flag.

6. Permissions and auth

Documents need ACLs. The WebSocket server has to verify a token on connect and authorize each document subscription. Hocuspocus has an onAuthenticate hook. With Liveblocks, you implement an auth endpoint that mints room tokens.

A common bug: assuming the auth check at connect time is enough. Re-check on document open, because permissions can change mid-session. If you are also gating sensitive data behind realtime channels, the patterns in our note on server-sent events vs WebSockets are worth a read before you commit to a transport.

Common pitfalls

Five mistakes we see in collaborative editor postmortems, almost every time:

  • No document size limit. Yjs docs grow with every edit. Without compaction, your 50-page contract becomes a 40MB blob after a year. Symptom: page load takes 8 seconds.
  • Awareness state leaking sensitive info. Awareness is broadcast to every connected client. If you put a user's email there for the avatar tooltip, every other user can read it. Use anonymized IDs and resolve names client-side.
  • Cursors that do not survive scroll. ProseMirror positions are document-relative, not viewport-relative. If your editor virtualizes long documents, you need a custom cursor renderer.
  • No bandwidth budget. Every keystroke broadcasts an update. On a flaky connection or with 20+ users in a room, this becomes painful. Use y-protocols/sync with batching, or rate-limit at the server.
  • Treating undo as single-user. Y.UndoManager is per-client by default, which is what you want. But you have to tell it which transactions to track (only the current user's, not collaborators'). Get this wrong and undo deletes your collaborator's work.

If you want a pre-launch sanity check on choices like the ones above, our tools/ship-or-skip grader will audit your stack against what actually ships in 2026.

When you can skip this entirely

Two cases where you should not build a collaborative editor at all.

You have fewer than 100 active users. Multiplayer adds 6 to 12 weeks of engineering work plus ongoing operational complexity (WebSocket servers, sticky sessions, document storage, presence debugging). For a tool with 50 users, a "refresh to see changes" pattern is fine, and you can revisit the decision when retention proves out.

Your data is not shared in real time. If users edit their own documents and occasionally share read-only links, you do not need CRDTs. Save the document on blur, render the share view server-side, done. This is the right pattern for most blog editors, form builders, and per-user dashboards. The same principle applies to most internal tooling, where the patterns in our guide on how to write production-grade tests in 2026 matter more than your sync engine.

What this looks like with a Cadence engineer

A senior engineer ($1,500/week on Cadence) can stand up the Yjs + Tiptap + Hocuspocus stack with presence, cursors, and a basic comment thread in 1 to 2 weeks. Comments, anchoring, and offline support add another 2 to 3 weeks. A lead ($2,000/week) is the right call if you need to architect for 10k+ concurrent rooms or design custom conflict resolution.

Every engineer on Cadence is AI-native by default, vetted on Cursor / Claude Code / Copilot fluency before they unlock bookings. For a CRDT integration where the docs are dense and the failure modes are subtle, that vetting matters: the median time-to-first-commit on our marketplace is 27 hours, but the actual differentiator is how fast they pattern-match on Yjs internals when the inevitable awareness bug shows up.

If you would rather not run the sync server yourself, start with Liveblocks for the first 90 days, then graduate to self-hosted Yjs + Hocuspocus once you cross 5,000 MAU. The Yjs document format is identical between the two; the migration is real but bounded.

Building multiplayer this quarter? Book a senior engineer on Cadence ($1,500/week, 48-hour free trial) who has shipped Yjs to production. Replace any week, no notice period.

FAQ

How long does it take to build a real-time collaborative editor?

A basic Yjs + Tiptap editor with presence and cursors takes a senior engineer 1 to 2 weeks. Full feature parity with Notion-level collaboration (comments, threads, offline sync, mobile) is a 3 to 6 month project for a small team. Hosted SDKs like Liveblocks cut the initial timeline in half.

Should I use CRDTs or OT for a new editor?

Use CRDTs (Yjs or Automerge) for new editors in 2026. They handle offline gracefully, scale horizontally, and have a healthier open-source ecosystem. OT is still the right call only for strict server-authoritative apps (legal, regulated finance) or when you need linear edit history for compliance.

What does Liveblocks cost compared to self-hosting Yjs?

Liveblocks is free up to 50 MAU, $29/mo at the Starter tier, and $299/mo and up for production teams. Self-hosting Yjs costs you a small VPS for Hocuspocus plus Postgres for storage ($20 to $100/mo at small scale) and 1 to 2 weeks of engineering setup. Liveblocks wins on time-to-launch; self-hosting wins on cost at scale.

Can I add real-time collaboration to an existing editor?

Yes, if the editor uses ProseMirror, Slate, CodeMirror 6, or Lexical. Tiptap (ProseMirror) has the best Yjs bindings. Slate has community bindings. CodeMirror has y-codemirror.next. If you built a custom editor on contenteditable, retrofitting CRDTs is harder than rewriting on a framework.

How do you handle offline edits in a collaborative editor?

Use y-indexeddb to mirror the Yjs document to the browser's IndexedDB. The library handles sync on reconnect automatically: it merges local edits with server state using the CRDT, with no conflict prompts needed. For longer offline sessions (days), add a "last synced" UI indicator so users know their work is not yet shared.

Japish Thind
Backend Developer

Backend developer at withRemote. Writes on API design, observability, and database trade-offs.

All posts