Documentation Index Fetch the complete documentation index at: https://docs.velt.dev/llms.txt
Use this file to discover all available pages before exploring further.
The @veltdev/tiptap-crdt-react library enables real-time collaborative editing on Tiptap Editors. The collaboration editing engine is built on top of Yjs and Velt SDK.
Prerequisites
Node.js (v14 or higher)
React (v16.8 or higher for hooks)
A Velt account with an API key (sign up )
Optional: TypeScript for type safety
Setup
Step 1: Install Dependencies
Install the required packages:
React / Next.js
Other Frameworks
npm install @veltdev/tiptap-crdt-react @tiptap/react @tiptap/starter-kit @tiptap/extension-collaboration @tiptap/extension-collaboration-cursor
npm install @veltdev/tiptap-crdt @veltdev/client @tiptap/core @tiptap/starter-kit @tiptap/extension-collaboration-caret
Step 2: Setup Velt
Initialize the Velt client by following the Velt Setup Docs . This is required for the collaboration engine to work.
Step 3: Initialize Velt CRDT Extension
Initialize the Velt CRDT extension and use it to initialize the Tiptap editor.
Pass an editorId to uniquely identify each editor instance you have in your app. This is especially important when you have multiple editors in your app.
React / Next.js
Other Frameworks
const { VeltCrdt } = useVeltTiptapCrdtExtension ({
editorId: 'YOUR_EDITOR_ID'
});
const editor = useEditor ({
extensions: [
StarterKit . configure ({
history: false ,
}),
... ( VeltCrdt ? [ VeltCrdt ] : []),
],
content: ''
}, [ VeltCrdt ]);
return < EditorContent editor = { editor } /> ;
// Create the CRDT store
const store = await createVeltTipTapStore ({
editorId: editorId ,
veltClient: veltClient ,
});
// Create TipTap editor with collaboration extensions
const editor = new Editor ({
element: editorContainer ,
extensions: [
StarterKit . configure ({
history: false , // Disable history as CRDT handles undo/redo
}),
store . getCollabExtension (),
CollaborationCaret . configure ({
provider: store . getStore (). getProvider (),
user: { name: user . name , color: user . color },
}),
],
content: '' ,
});
Step 4: Add CSS for Collaboration Cursor
Add the following CSS to your app to style the collaboration cursor.
/* Collaboration cursor styling */
.collaboration-cursor__caret ,
.collaboration-carets__caret {
border-left : 1 px solid #0d0d0d ;
border-right : 1 px solid #0d0d0d ;
margin-left : -1 px ;
margin-right : -1 px ;
pointer-events : none ;
position : relative ;
word-break : normal ;
}
/* Render the username above the caret */
.collaboration-cursor__label ,
.collaboration-carets__label {
border-radius : 3 px 3 px 3 px 0 ;
color : #0d0d0d ;
font-size : 12 px ;
font-style : normal ;
font-weight : 600 ;
left : -1 px ;
line-height : normal ;
padding : 0.1 rem 0.3 rem ;
position : absolute ;
top : -1.4 em ;
user-select : none ;
white-space : nowrap ;
}
Notes
Unique editorId : Use a unique editorId per editor instance.
Disable history : Turn off Tiptap history when using collaboration.
Auth required : Ensure the Velt client is initialized and user is available before starting.
Testing and Debugging
To test collaboration:
Login two unique users on two browser profiles and open the same page in both.
Edit in one window and verify changes and cursors appear in the other.
Common issues:
Cursors not appearing: Ensure each editor has a unique editorId and users are authenticated. Also ensure that you are logged in with two different users in two different browser profiles.
Editor not loading: Confirm the Velt client is initialized and the API key is valid.
Desynced content: Make sure Tiptap history is disabled when using collaboration.
Enable browser console warnings to see detailed debugging information. The Velt SDK logs helpful warnings and errors to the console that can help you troubleshoot issues quickly. You can also use the VeltCrdtStoreMap debugging interface to inspect and monitor your CRDT stores in real-time.
Complete Example
Open Live Demo In Larger Window | View Demo Repo import { EditorContent , useEditor } from '@tiptap/react' ;
import StarterKit from '@tiptap/starter-kit' ;
import { useVeltTiptapCrdtExtension } from '@veltdev/tiptap-crdt-react' ;
import React from 'react' ;
const CollaborativeEditor : React . FC = () => {
const { VeltCrdt } = useVeltTiptapCrdtExtension ({
editorId: 'velt-tiptap-crdt-demo'
});
// Initialize the editor with our collaboration extension
const editor = useEditor ({
extensions: [
StarterKit . configure ({
history: false ,
}),
... ( VeltCrdt ? [ VeltCrdt ] : []),
],
content: ''
}, [ VeltCrdt ]);
return (
< div className = "editor-container" >
< div className = "editor-content" >
< EditorContent editor = { editor } />
</ div >
< div className = "status" >
{ VeltCrdt ? 'Connected to collaborative session' : 'Connecting to collaborative session...' }
</ div >
</ div >
);
};
export default CollaborativeEditor ;
See all 33 lines
Open Live Demo In Larger Window | View Demo Repo // Import required modules
import { initVelt } from '@veltdev/client' ;
import { createVeltTipTapStore } from '@veltdev/tiptap-crdt' ;
import { Editor } from '@tiptap/core' ;
import StarterKit from '@tiptap/starter-kit' ;
import CollaborationCaret from '@tiptap/extension-collaboration-caret' ;
// Step 1: Initialize Velt client
const veltClient = await initVelt ( 'YOUR_API_KEY' );
// Step 2: Authenticate user
const user = { userId: 'user-1' , name: 'John Doe' , color: '#3b82f6' };
await veltClient . identify ( user );
// Step 3: Set document
await veltClient . setDocument ( 'my-document-id' );
// Step 4: Create CRDT store
const store = await createVeltTipTapStore ({
editorId: 'velt-tiptap-crdt-demo' ,
veltClient: veltClient ,
});
// Step 5: Create TipTap editor
const editor = new Editor ({
element: document . getElementById ( 'editor' ),
extensions: [
StarterKit . configure ({ history: false }),
store . getCollabExtension (),
CollaborationCaret . configure ({
provider: store . getStore (). getProvider (),
user: { name: user . name , color: user . color },
}),
],
content: '' ,
});
// Cleanup on unmount
editor . destroy ();
store . destroy ();
See all 40 lines
APIs
Custom Encryption
Encrypt CRDT data before it’s stored in Velt by registering a custom encryption provider. For CRDT methods, input data is of type Uint8Array | number[].
React / Next.js
Other Frameworks
async function encryptData ( config : EncryptConfig < number []>) : Promise < string > {
const encryptedData = await yourEncryptDataMethod ( config . data );
return encryptedData ;
}
async function decryptData ( config : DecryptConfig < string >) : Promise < number []> {
const decryptedData = await yourDecryptDataMethod ( config . data );
return decryptedData ;
}
const encryptionProvider : VeltEncryptionProvider < number [], string > = {
encrypt: encryptData ,
decrypt: decryptData ,
};
< VeltProvider
apiKey = "YOUR_API_KEY"
encryptionProvider = { encryptionProvider }
/>
async function encryptData ( config : EncryptConfig < number []>) : Promise < string > {
const encryptedData = await yourEncryptDataMethod ( config . data );
return encryptedData ;
}
async function decryptData ( config : DecryptConfig < string >) : Promise < number []> {
const decryptedData = await yourDecryptDataMethod ( config . data );
return decryptedData ;
}
const encryptionProvider : VeltEncryptionProvider < number [], string > = {
encrypt: encryptData ,
decrypt: decryptData ,
};
client . setEncryptionProvider ( encryptionProvider );
See also: setEncryptionProvider()
· VeltEncryptionProvider
· EncryptConfig
· DecryptConfig
Initialization
React / Next.js
Other Frameworks
React hook that provides real-time collaborative editing on Tiptap editor with Yjs and Velt SDK.
Signature: useVeltTiptapCrdtExtension(config: VeltTiptapCrdtExtensionConfig)
Params: VeltTiptapCrdtExtensionConfig
editorId: Unique identifier for the editor instance.
initialContent: Initial content for the editor.
debounceMs: Debounce time for update propagation (ms).
Returns: VeltTiptapCrdtExtensionResponse
VeltCrdt: Tiptap Velt CRDT Extension.
store: Tiptap CRDT store instance.
isLoading: Whether the store is initialized. false once store is initialized, true by default.
const { VeltCrdt , store , isLoading } = useVeltTiptapCrdtExtension ({
editorId: 'velt-tiptap-crdt-demo'
});
Factory function that creates and initializes a VeltTipTapStore instance for collaborative editing.
Signature: createVeltTipTapStore(config: VeltTipTapStoreConfig): Promise<VeltTipTapStore | null>
Params: VeltTipTapStoreConfig
editorId: Unique identifier for the editor instance.
veltClient: Velt client instance (from initVelt()).
initialContent: Optional initial content for the editor.
debounceMs: Optional debounce time for update propagation (ms).
Returns: Promise<VeltTipTapStore | null> - The store instance or null if creation fails.
import { createVeltTipTapStore } from '@veltdev/tiptap-crdt' ;
const store = await createVeltTipTapStore ({
editorId: 'velt-tiptap-crdt-demo' ,
veltClient: client ,
initialContent: '<p>Start collaborating...</p>' ,
});
Get the Tiptap collaboration extension bound to the current Yjs document. const collabExtension = store . getCollabExtension ();
Store Methods
Access the underlying CRDT store managing document state.
const crdtStore = store . getStore ();
Tear down the store and clean up listeners/resources.
Accessor for the underlying Yjs document.
const ydoc = store . getYDoc ();
Returns the Y.XmlFragment instance that handles Tiptap’s rich text structure.
Returns: Y.XmlFragment | null
const yxml = store . getYXml ();
Restores the document to a specific version state. Useful for implementing undo/redo functionality or version control features.
Signature: setStateFromVersion(version: Version): Promise<void>
Params:
version: The version object to restore the document to.
Returns: Promise<void>
// Restore document to a previous version
await store . setStateFromVersion ( previousVersion );