Collaboration
Sync a document between clients with Yjs and render remote cursors.
@inksane/extension-collaboration connects an editor to a shared Yjs document. Document changes
sync through a Y.Text, and remote cursors render through Yjs awareness with the user’s name next
to the caret.
pnpm add @inksane/extension-collaboration yjsUsage
import { Editor } from "@inksane/core";
import { BaseKit } from "@inksane/core/extensions/base-kit";
import { CollaborationExtension } from "@inksane/extension-collaboration";
import * as Y from "yjs";
const ydoc = new Y.Doc();
const editor = new Editor({
element: document.getElementById("editor"),
content: "# Shared document",
extensions: [
BaseKit({ history: false }),
CollaborationExtension({
ydoc,
user: { name: "Alice", color: "#30bced" },
}),
],
});BaseKit provides the Markdown features and standard editing shortcuts. Disable its history
feature because Yjs owns undo and redo. You can also choose individual extensions, leaving out
HistoryExtension.
Options
| Option | Type | Default | Description |
|---|---|---|---|
ydoc | Y.Doc | required | The document shared by every collaborator. |
field | string | "content" | Name of the shared Y.Text field. |
awareness | Awareness | new one on ydoc | Broadcasts cursors. Pass your provider’s instance. |
user | Partial<CollaborationUser> | anonymous | Identity shown on your cursor to everyone else. |
user accepts name, color, and an optional colorLight for the selection highlight. When you
omit them, the extension derives a color from the awareness client id and uses it for the caret and
the colorLight tint.
Transport is up to you
The extension only connects the editor to a Y.Doc and an Awareness. Sending updates over the
network is the job of a provider, for example y-websocket, y-webrtc, or your own server:
import { WebsocketProvider } from "y-websocket";
const ydoc = new Y.Doc();
const provider = new WebsocketProvider("wss://example.com", "my-room", ydoc);
const editor = new Editor({
element: document.getElementById("editor"),
extensions: [
BaseKit({ history: false }),
CollaborationExtension({
ydoc,
awareness: provider.awareness,
user: { name: "Alice", color: "#30bced" },
}),
],
});Who seeds the document
The Y.Doc owns the document. When the shared text is still empty and the editor was created with
content, the extension seeds the shared text with it, so the first client creates the document.
A client joining an existing room should pass the current shared text as content.
Local checks
Two editors in the same page that share a Y.Doc sync immediately, which is a quick way to try the
extension without a server. The extension tests in the repository do exactly that: they link two
documents and two awareness instances.
The extension sets priority: 100, so it initializes before BaseKit’s default-priority features.