Recipe: Collaborative Editor
Audience: users.
Goal: combine room lifecycle, awareness, and Yjs CRDT primitives for document editing.
Scenario
Section titled “Scenario”A Notion-style editor where users can:
- edit content concurrently
- see collaborator cursors/identity
- view typing/focus signals
Example
Section titled “Example”import { createRoom } from '@roomful/core';import * as Y from 'yjs';
const room = createRoom(`doc-${documentId}`, { transport: 'auto', presence: { name: user.name, color: user.color },});
await room.connect();
const ydoc: Y.Doc = room.getYDoc();const provider = room.getYProvider();
// CodeMirror-style editors usually bind to a shared Y.Text.const codeMirrorText = ydoc.getText('content');
// ProseMirror-style editors usually bind to a shared Y.XmlFragment.const proseMirrorRoot = ydoc.getXmlFragment('prosemirror');
// Use the shared Yjs awareness instance for selections and collaborator state.const awareness = provider.awareness;Integration Notes
Section titled “Integration Notes”- Pass
codeMirrorTextandawarenessto your CodeMirror Yjs binding. - Pass
proseMirrorRootandawarenessto your ProseMirror Yjs binding. room.getYDoc()androom.getYProvider()are singletons per room instance, so reuse them across editor mounts.provider.syncedbecomestrueafter the room finishes its initial Yjs sync handshake with connected peers.- Keep non-document metadata in shared state if you want a simpler
lwwor structured CRDT model beside the editor document.
Failure Modes
Section titled “Failure Modes”- Connection interruption: rely on reconnection and replay behavior.
- Late joiners: wait for
provider.syncedbefore assuming the full document is present locally. - Conflicting metadata writes: use
lwwfor simple title/tag fields.