Skip to main content

Overview

The @yoopta/collaboration package enables real-time collaborative editing in Yoopta Editor. Multiple users can edit the same document simultaneously with automatic conflict resolution powered by Yjs CRDT. The package provides:
  • withCollaboration extension — connects your editor to a collaboration server
  • Collaborative undo/redo — Cmd+Z only undoes your own changes, not other users’
  • Remote cursors — see where other users are editing
  • Awareness — track connected users and their presence
  • React hooks — access collaboration state in your components
The collaboration client is fully open-source and works with any Yjs-compatible WebSocket server. You can self-host with your own infrastructure or use Yoopta Cloud for a managed solution with version history, permissions, and more.

Installation

@yoopta/collaboration requires @yoopta/editor >= 6.0.0 and React >= 18.2.0 as peer dependencies.

Quick Start

Wrap your editor with withCollaboration — similar to how withMentions works:
Always call editor.collaboration.destroy() on cleanup to properly close the WebSocket connection and remove awareness states.

Configuration

withCollaboration(editor, config)

Extends a YooEditor instance with collaboration capabilities. Returns a CollaborationYooEditor.
YooEditor
required
The editor instance from createYooptaEditor().
CollaborationConfig
required
Configuration object for the collaboration session.

CollaborationConfig

string
required
WebSocket server URL. Example: "wss://collab.yoopta.cloud"
string
required
Unique identifier for the document/room. All users with the same roomId will collaborate on the same document.
CollaborationUser
required
Information about the current user. Used for cursor labels and presence indicators.
string
Authentication token sent to the server on connection. Use this to verify users and check subscription access on the server side.
YooptaContentValue
Initial content to seed the document if no remote state exists. When the first user connects to an empty room, this value populates the shared document.
boolean
default:"true"
Whether to connect to the server immediately. Set to false to connect manually later via editor.collaboration.connect().
Y.Doc
Optional: provide your own Yjs Y.Doc instance. If not provided, one is created automatically. Useful for advanced integrations or testing.

Editor API

After applying withCollaboration, the editor gains an editor.collaboration namespace:

editor.collaboration.state

Read-only access to the current collaboration state.

editor.collaboration.connect()

Manually connect to the WebSocket server. Only needed if connect: false was passed in config.

editor.collaboration.disconnect()

Disconnect from the server. The editor remains usable for local editing.

editor.collaboration.destroy()

Disconnect and clean up all resources (WebSocket, observers, awareness). Call this on component unmount.

editor.collaboration.getDocument()

Returns the underlying Y.Doc instance for advanced use cases.

Components

<RemoteCursors />

Renders visual indicators showing which block each remote user is currently editing.
Must be rendered as a child of <YooptaEditor>. Displays a colored bar and name label next to the block each remote user is editing.

Hooks

All hooks must be used within a <YooptaEditor> component that has collaboration enabled.

useCollaboration()

Returns the full collaboration state. Re-renders on any state change.

useConnectionStatus()

Returns only the connection status string. Lighter than useCollaboration() when you only need the status.

useRemoteCursors()

Returns an array of remote cursor data for all connected users.

Deferred Connection

If you need to connect after the editor is rendered (e.g., after authentication), set connect: false:

Custom Y.Doc

For advanced use cases (e.g., testing two editors in the same page), you can provide your own Y.Doc:

Self-Hosted Setup

@yoopta/collaboration uses Yjs only as a sync layer — it does not store your documents. Your content remains standard Yoopta JSON (YooptaContentValue) that you store in your own database.
The Y.Doc is ephemeral — it only lives during the collaboration session. When all users disconnect, it can be discarded. Your database is the source of truth.

Persisting to Your Database

Load content from your database as initialValue, and save back on change:
The initialValue is only used when the first user connects to an empty room. Once the Y.Doc has content (from any client), subsequent connections load from the Y.Doc state, not from initialValue.

Running Your Own WebSocket Server

Any Yjs-compatible WebSocket server works. The simplest option is y-websocket:
For production, consider Hocuspocus which adds authentication, persistence hooks, and webhook support out of the box.
For small teams (2-5 concurrent editors), a single y-websocket process is more than sufficient. No special infrastructure needed.

Yoopta Cloud

For teams that don’t want to run their own collaboration infrastructure, Yoopta Cloud provides a managed solution:
  • Managed WebSocket infrastructure with automatic scaling
  • Built-in document persistence and version history
  • Authentication and room-level permissions
  • Comments and threads
  • REST API for document access
  • Webhooks for integrations (search indexing, backups, workflows)
  • Analytics dashboard
Yoopta Cloud is coming soon. The self-hosted path described above will always remain free and open-source.

How It Works

Under the hood, @yoopta/collaboration maintains a bidirectional binding between the Yoopta editor and a Yjs shared document:
  • Local changes flow through editor.applyTransforms and are automatically synced to the Y.Doc, then sent to the server via WebSocket.
  • Remote changes arrive via WebSocket, update the Y.Doc, and are applied to the editor without entering the local undo/redo history.
  • Conflict resolution is handled by the Yjs CRDT — concurrent edits are merged automatically at the character level.
  • Undo/redo uses Yjs UndoManager to selectively reverse only local changes (see below).

Undo / Redo

When collaboration is active, editor.undo() and editor.redo() (Cmd+Z / Cmd+Shift+Z) automatically use Yjs UndoManager instead of the default history stack. This means:
  • Only your changes are undone — if Alice types “Hello” and Bob types “World”, Alice pressing Cmd+Z removes only “Hello”. Bob’s text stays intact.
  • Works at all levels — text edits, block insertions, deletions, splits, merges, formatting, and metadata changes are all tracked.
  • No extra setup neededwithCollaboration overrides undo/redo automatically. When collaboration is destroyed, the default history behavior is restored.
Rapid edits (e.g., continuous typing) are batched into a single undo step, similar to how undo works in Google Docs or Notion.
The initial document content (loaded from the server or seeded via initialValue) is not undoable — only edits made after the document loads can be undone.

TypeScript

All types are exported from the package:

WebSocketProvider

For advanced server integrations, the WebSocketProvider class is exported:
This is the transport layer used internally. It implements the Yjs sync and awareness protocols over WebSocket with automatic reconnection and authentication support.