Real-time collaborative editing extension for Tiptap using Yjs CRDT
npx @tessl/cli install tessl/npm-tiptap--extension-collaboration@3.4.0The collaboration extension enables real-time collaborative editing in Tiptap editors using Yjs CRDT (Conflict-free Replicated Data Type). It provides seamless synchronization between multiple users editing the same document simultaneously, with built-in undo/redo functionality and conflict resolution. The extension runs at high priority (1000) to ensure proper initialization before other extensions.
npm install @tiptap/extension-collaborationimport Collaboration from "@tiptap/extension-collaboration";Or named import:
import { Collaboration } from "@tiptap/extension-collaboration";For CommonJS:
const Collaboration = require("@tiptap/extension-collaboration").default;import { Editor } from "@tiptap/core";
import Collaboration from "@tiptap/extension-collaboration";
import * as Y from "yjs";
// Create a Y.js document
const ydoc = new Y.Doc();
// Configure the editor with collaboration
const editor = new Editor({
extensions: [
// ... other extensions
Collaboration.configure({
document: ydoc,
field: "default", // optional: Y.js fragment name
}),
],
});
// Typically used with a collaboration provider
import { WebrtcProvider } from "y-webrtc";
const provider = new WebrtcProvider("room-name", ydoc);The collaboration extension integrates several key components:
The main extension that enables real-time collaborative editing with Y.js integration.
/**
* Collaboration extension for real-time collaborative editing
*/
const Collaboration: Extension<CollaborationOptions, CollaborationStorage>;
interface CollaborationOptions {
/** Y.js document instance */
document?: Doc | null;
/** Y.js fragment name (default: 'default') */
field?: string;
/** Raw Y.js fragment (alternative to document + field) */
fragment?: XmlFragment | null;
/** Collaboration provider instance */
provider?: any | null;
/** Callback fired when content is initially rendered from Y.js */
onFirstRender?: () => void;
/** Options for the Y.js sync plugin */
ySyncOptions?: YSyncOpts;
/** Options for the Y.js undo plugin */
yUndoOptions?: YUndoOpts;
}
interface CollaborationStorage {
/** Whether collaboration is currently disabled */
isDisabled: boolean;
}Usage Example:
import Collaboration from "@tiptap/extension-collaboration";
import * as Y from "yjs";
const ydoc = new Y.Doc();
const editor = new Editor({
extensions: [
Collaboration.configure({
document: ydoc,
field: "content", // custom fragment name
onFirstRender: () => {
console.log("Initial content loaded from Y.js");
},
}),
],
});The extension adds undo and redo commands to the editor with collaborative awareness.
/**
* Undo recent changes in collaborative context
*/
editor.commands.undo(): boolean;
/**
* Reapply reverted changes in collaborative context
*/
editor.commands.redo(): boolean;Usage Example:
// Programmatic undo/redo
editor.commands.undo();
editor.commands.redo();
// Check if undo/redo is available
const canUndo = editor.can().undo();
const canRedo = editor.can().redo();Utility function to check if a ProseMirror transaction originated from a Y.js change.
/**
* Checks if a transaction was originated from a Yjs change
* @param transaction - The ProseMirror transaction to check
* @returns True if the transaction originated from Yjs change
*/
function isChangeOrigin(transaction: Transaction): boolean;Usage Example:
import { isChangeOrigin } from "@tiptap/extension-collaboration";
editor.on("transaction", ({ transaction }) => {
if (isChangeOrigin(transaction)) {
console.log("This change came from another collaborator");
} else {
console.log("This change was made locally");
}
});Access collaboration state through the editor's storage system.
/**
* Access collaboration storage
*/
editor.storage.collaboration: CollaborationStorage;Usage Example:
// Check if collaboration is disabled
const isDisabled = editor.storage.collaboration.isDisabled;
// Disable collaboration (prevents sync with other users)
editor.storage.collaboration.isDisabled = true;// Y.js types (from yjs package)
interface Doc {
getXmlFragment(name: string): XmlFragment;
destroy(): void;
}
interface XmlFragment {
doc?: Doc;
}
interface UndoManager {
undoStack: any[];
redoStack: any[];
trackedOrigins: Set<any>;
restore?: () => void;
doc: Doc;
afterTransactionHandler: (transaction: any, doc: Doc) => void;
_observers: any;
}
// ProseMirror types (from @tiptap/pm packages)
interface Transaction {
getMeta(key: any): any;
setMeta(key: string, value: any): Transaction;
}
// Plugin configuration types
type YSyncOpts = Parameters<typeof ySyncPlugin>[1];
type YUndoOpts = Parameters<typeof yUndoPlugin>[0];The extension includes content validation when editor.options.enableContentCheck is enabled. This validates Y.js document content against the ProseMirror schema before applying transactions:
editor.on("contentError", ({ error, editor, disableCollaboration }) => {
console.error("Collaboration content error:", error);
// Optionally disable collaboration to prevent further issues
disableCollaboration();
});When content validation is enabled, the extension:
contentError events when invalid content is detecteddisableCollaboration() callback to stop synchronizationImportant: This extension includes its own undo/redo functionality and is not compatible with @tiptap/extension-undo-redo. Using both extensions together will cause conflicts. The collaboration extension will log a warning if it detects the presence of the undoRedo extension.
The extension automatically adds collaborative undo/redo keyboard shortcuts:
Peer Dependencies:
@tiptap/core - Core Tiptap framework@tiptap/pm - ProseMirror packages for Tiptap@tiptap/y-tiptap - Y.js integration for Tiptapyjs - Y.js CRDT libraryProvider Examples:
y-webrtc - WebRTC-based collaborationy-websocket - WebSocket-based collaborationy-indexeddb - Local persistence