> ## Documentation Index
> Fetch the complete documentation index at: https://velt.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# BlockNote Editor

> Setup Multiplayer Editing for BlockNote Editor.

The `@veltdev/blocknote-crdt-react` and `@veltdev/blocknote-crdt` libraries enable real-time collaborative editing on BlockNote Editors. The collaboration editing engine is built on top of [Yjs](https://docs.yjs.dev/) 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](https://console.velt.dev/))
* Optional: TypeScript for type safety

## Setup

### Step 1: Install Dependencies

Install the required packages:

> [tab] React / Next.js
>
> ```bash theme={null}
> npm install @veltdev/blocknote-crdt-react @veltdev/blocknote-crdt @veltdev/react @veltdev/types @blocknote/core @blocknote/react @blocknote/mantine yjs
> ```

> [tab] Other Frameworks
>
> ```bash theme={null}
> npm install @veltdev/blocknote-crdt @veltdev/client @blocknote/core yjs
> ```

### Step 2: Setup Velt

Initialize the Velt client by following the [Velt Setup Docs](https://velt.dev/docs/get-started/quickstart.md). This is required for the collaboration engine to work.

### Step 3: Initialize Collaborative Editor

* Initialize the collaboration manager and use it to create the BlockNote 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.

> [tab] React / Next.js
>
> Use the `useCollaboration` hook to manage the entire CRDT lifecycle. It creates a `CollaborationManager`, initializes the CRDT store, and returns a `BlockNoteCollaborationConfig`. You pass this config to `useCreateBlockNote({ collaboration: ... })` when creating your BlockNote editor.
>
> ```tsx theme={null}
> import { useCollaboration } from '@veltdev/blocknote-crdt-react';
> import { useCreateBlockNote } from '@blocknote/react';
> import { BlockNoteView } from '@blocknote/mantine';
> import '@blocknote/mantine/style.css';
>
> function BlockNoteEditorComponent() {
>   const {
>     collaborationConfig,
>     isLoading,
>     isSynced,
>     status,
>     error,
>     manager,
>     saveVersion,
>     getVersions,
>     restoreVersion,
>   } = useCollaboration({
>     editorId: 'my-blocknote-editor',
>     onError: (err) => console.error('Collaboration error:', err),
>   });
>
>   const editor = useCreateBlockNote(
>     collaborationConfig
>       ? { collaboration: collaborationConfig }
>       : {},
>     [collaborationConfig],
>   );
>
>   if (error) return <div>Error: {error.message}</div>;
>   if (isLoading || !collaborationConfig) return <div>Connecting...</div>;
>
>   return <BlockNoteView editor={editor} theme="light" />;
> }
> ```
>
> > [!NOTE]
> >
> > When the `collaboration` config is provided, BlockNote automatically uses the Y.XmlFragment as the document source, enables remote cursor rendering, and switches undo/redo to Yjs UndoManager. No additional configuration is needed.

> [tab] Other Frameworks
>
> Use `createCollaboration()` to create a `CollaborationManager`, then call `manager.getCollaborationConfig()` to get the config object for BlockNote. Pass this to `BlockNoteEditor.create({ collaboration: ... })`.
>
> ```js theme={null}
> import { createCollaboration } from '@veltdev/blocknote-crdt';
> import { BlockNoteEditor } from '@blocknote/core';
>
> // Wait for the SDK to be ready
> client.getVeltInitState().subscribe(async (isReady) => {
>   if (!isReady) return;
>
>   // Create the CollaborationManager
>   const manager = await createCollaboration({
>     editorId: 'my-blocknote-editor',
>     veltClient: client,
>     initialContent: [
>       { type: 'paragraph', content: 'Welcome to the collaborative editor!' },
>     ],
>     onError: (error) => console.error('Collaboration error:', error),
>   });
>
>   // Get the collaboration config for BlockNote
>   const collabConfig = manager.getCollaborationConfig();
>
>   // Create the BlockNote editor
>   const editor = BlockNoteEditor.create({
>     collaboration: collabConfig,
>   });
>
>   editor.mount(document.getElementById('editor'));
> });
> ```
>
> > [!NOTE]
> >
> > `initialContent` is applied exactly once — only when the document is brand new (empty). On subsequent loads, the persisted content is used instead.
>
> > [!NOTE]
> >
> > When the `collaboration` config is provided, BlockNote automatically uses the Y.XmlFragment as the document source, enables remote cursor rendering, and switches undo/redo to Yjs UndoManager.

### Step 4: Status Monitoring (Optional)

Monitor the connection status and sync state to display UI indicators.

> [tab] React / Next.js
>
> The `useCollaboration` hook returns reactive `status`, `isSynced`, and `error` state:
>
> ```tsx theme={null}
> const { collaborationConfig, isLoading, isSynced, status, error } = useCollaboration({
>   editorId: 'my-blocknote-editor',
> });
>
> if (error) return <div>Error: {error.message}</div>;
> if (isLoading) return <div>Connecting...</div>;
>
> // In your JSX
> <div>Status: {status} | Synced: {isSynced ? 'Yes' : 'No'}</div>
> ```

> [tab] Other Frameworks
>
> Subscribe to status and sync changes on the `manager`:
>
> ```js theme={null}
> // Connection status: 'connecting' | 'connected' | 'disconnected'
> manager.onStatusChange((status) => {
>   console.log('Status:', status);
> });
>
> // Sync state: true when initial sync is complete
> manager.onSynced((synced) => {
>   console.log('Synced:', synced);
> });
>
> // Read current values at any time
> console.log(manager.status);       // 'connected'
> console.log(manager.synced);       // true
> console.log(manager.initialized);  // true
> ```

### Step 5: Version Management (Optional)

Save and restore named snapshots of the document state.

> [tab] React / Next.js
>
> Version methods are returned directly from `useCollaboration` as first-class APIs:
>
> ```tsx theme={null}
> // Save a version
> const versionId = await saveVersion('Before major edit');
>
> // List all versions
> const versions = await getVersions();
>
> // Restore a specific version
> await restoreVersion(versions[0].versionId);
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> // Save a named snapshot
> const versionId = await manager.saveVersion('Draft v1');
>
> // List all saved versions
> const versions = await manager.getVersions();
> // Returns: [{ versionId, versionName, timestamp }, ...]
>
> // Restore to a saved version
> await manager.restoreVersion(versionId);
> ```

### Step 6: Force Reset Initial Content (Optional)

By default, `initialContent` is applied exactly once — only when the document is brand new. Pass `forceResetInitialContent: true` to always overwrite remote data with `initialContent` on initialization.

> [!WARNING]
>
> `forceResetInitialContent` wipes all existing content every time the page loads with this flag. Do not use it in production user flows — it is intended for development and admin use only.

> [tab] React / Next.js
>
> ```tsx theme={null}
> const collab = useCollaboration({
>   editorId: 'my-blocknote-editor',
>   initialContent: [{ type: 'paragraph', content: 'Fresh start!' }],
>   forceResetInitialContent: true,
> });
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> const manager = await createCollaboration({
>   editorId: 'my-blocknote-editor',
>   veltClient: client,
>   initialContent: [{ type: 'paragraph', content: 'Fresh start' }],
>   forceResetInitialContent: true,
> });
> ```

### Step 7: CRDT Event Subscription (Optional)

Listen for real-time sync events using `getCrdtElement().on()` from the Velt client.

> [tab] React / Next.js
>
> ```tsx theme={null}
> import { useVeltClient } from '@veltdev/react';
>
> const { client } = useVeltClient();
>
> useEffect(() => {
>   if (!client) return;
>   const crdtElement = client.getCrdtElement();
>   const sub = crdtElement.on('updateData').subscribe((event) => {
>     if (event) console.log('CRDT update:', event);
>   });
>   return () => sub?.unsubscribe?.();
> }, [client]);
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> const crdtElement = client.getCrdtElement();
>
> crdtElement.on('updateData').subscribe((event) => {
>   if (event) {
>     console.log('CRDT data updated:', event);
>   }
> });
> ```

### Step 8: Custom Encryption (Optional)

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[]`.

> [tab] React / Next.js
>
> ```ts theme={null}
> const encryptionProvider = {
>   encrypt: async ({ data }) => data.map((n) => n.toString()).join('__'),
>   decrypt: async ({ data }) => data.split('__').map((s) => parseInt(s, 10)),
> };
>
> <VeltProvider apiKey="YOUR_API_KEY" encryptionProvider={encryptionProvider}>
>   ...
> </VeltProvider>
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> const encryptionProvider = {
>   encrypt: async (config) => {
>     return config.data.map((num) => num.toString()).join('__');
>   },
>   decrypt: async (config) => {
>     return config.data.split('__').map((str) => parseInt(str, 10));
>   },
> };
>
> client.setEncryptionProvider(encryptionProvider);
> ```

See also: [setEncryptionProvider()](https://velt.dev/docs/api-reference/sdk/api/api-methods.md#setencryptionprovider-encryptionprovider)
· [VeltEncryptionProvider](https://velt.dev/docs/api-reference/sdk/models/data-models.md#veltencryptionprovider)
· [EncryptConfig](https://velt.dev/docs/api-reference/sdk/models/data-models.md#encryptconfig)
· [DecryptConfig](https://velt.dev/docs/api-reference/sdk/models/data-models.md#decryptconfig)

### Step 9: Error Handling (Optional)

> [tab] React / Next.js
>
> Pass an `onError` callback to handle initialization or runtime errors. The `error` reactive state is also available for rendering.
>
> ```tsx theme={null}
> const collab = useCollaboration({
>   editorId: 'my-blocknote-editor',
>   onError: (error) => console.error('Collaboration error:', error),
> });
>
> if (collab.error) {
>   return <div>Error: {collab.error.message}</div>;
> }
> ```

> [tab] Other Frameworks
>
> Pass an `onError` callback when creating the CollaborationManager:
>
> ```js theme={null}
> const manager = await createCollaboration({
>   editorId: 'my-blocknote-editor',
>   veltClient: client,
>   onError: (error) => console.error('Collaboration error:', error),
> });
> ```

### Step 10: Access Yjs Internals (Advanced)

The manager exposes escape hatches for direct Yjs manipulation when needed.

> [tab] React / Next.js
>
> ```tsx theme={null}
> // From the manager returned by useCollaboration
> const doc = manager.getDoc();
> const xml = manager.getXmlFragment();
> const awareness = manager.getAwareness();
> const provider = manager.getProvider();
> const store = manager.getStore();
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> import * as Y from 'yjs';
>
> const doc = manager.getDoc();
> const xml = manager.getXmlFragment();
> const provider = manager.getProvider();
> const awareness = manager.getAwareness();
> const store = manager.getStore();
> ```

### Step 11: Cleanup

Cleanup is handled by calling `manager.destroy()`, which cascades to the store, provider, and all listeners. Safe to call multiple times.

> [tab] React / Next.js
>
> ```tsx theme={null}
> // Automatic on component unmount via useEffect cleanup
> // Or manually:
> manager.destroy();
> ```

> [tab] Other Frameworks
>
> ```js theme={null}
> manager.destroy();
> ```

## Notes

* **Unique editorId**: Use a unique `editorId` per editor instance.
* **Pass collaborationConfig**: Provide the `collaboration` config from `useCollaboration` (React) or `manager.getCollaborationConfig()` (non-React) to the BlockNote editor.
* **Undo/redo**: Automatically switches to Yjs UndoManager when collaboration is enabled.

## Testing and Debugging

**To test collaboration:**

1. Login two unique users on two browser profiles and open the same page in both.
2. 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.

> [!TIP]
>
> Use the [VeltCrdtStoreMap debugging interface](https://velt.dev/docs/realtime-collaboration/crdt/setup/core.md#veltcrdtstoremap) to inspect and monitor your CRDT stores in real-time from the browser console.

## Complete Example

> [tab] React / Next.js
>
> A complete collaborative BlockNote editor with user login, status display, and version management.
>
> **App.tsx**
>
> ```tsx Complete App.tsx expandable lines theme={null}
> import { useMemo } from 'react';
> import { VeltProvider, useSetDocument, useCurrentUser } from '@veltdev/react';
> import { Header } from './Header';
> import { EditorComponent } from './Editor';
>
> const API_KEY = 'YOUR_API_KEY';
>
> function getDocumentParams() {
>   const params = new URLSearchParams(window.location.search);
>   return {
>     documentId: params.get('documentId') || 'blocknote-crdt-react-demo-doc-1',
>     documentName: params.get('documentName') || 'BlockNote CRDT React Demo',
>   };
> }
>
> function AppContent() {
>   const veltUser = useCurrentUser();
>   const { documentId, documentName } = useMemo(() => getDocumentParams(), []);
>   useSetDocument(documentId, { documentName });
>
>   return (
>     <div className="app-container">
>       <Header />
>       <main className="app-content">
>         <EditorComponent key={veltUser?.userId || '__none__'} />
>       </main>
>     </div>
>   );
> }
>
> export const App = () => (
>   <VeltProvider apiKey={API_KEY}>
>     <AppContent />
>   </VeltProvider>
> );
> ```
>
> **Editor.tsx**
>
> ```tsx Complete Editor.tsx expandable lines theme={null}
> import { useEffect, useState, useCallback, useMemo } from 'react';
> import { useVeltClient, useCurrentUser } from '@veltdev/react';
> import { useCollaboration } from '@veltdev/blocknote-crdt-react';
> import { useCreateBlockNote } from '@blocknote/react';
> import { BlockNoteView } from '@blocknote/mantine';
> import '@blocknote/mantine/style.css';
> import type { Version } from '@veltdev/crdt';
>
> function getEditorParams() {
>   const params = new URLSearchParams(window.location.search);
>   return {
>     documentId: params.get('documentId') || 'blocknote-crdt-react-demo-doc-1',
>     forceReset: params.get('forceResetInitialContent') === 'true',
>   };
> }
>
> export const EditorComponent = () => {
>   const veltUser = useCurrentUser();
>   const [versions, setVersions] = useState<Version[]>([]);
>   const [versionInput, setVersionInput] = useState('');
>
>   const params = useMemo(() => getEditorParams(), []);
>
>   const {
>     collaborationConfig,
>     isLoading,
>     isSynced,
>     status,
>     manager,
>     saveVersion,
>     getVersions,
>     restoreVersion,
>   } = useCollaboration({
>     editorId: params.documentId,
>     forceResetInitialContent: params.forceReset,
>     onError: (err) => console.error('Collaboration error:', err),
>   });
>
>   const editor = useCreateBlockNote(
>     collaborationConfig
>       ? {
>           collaboration: collaborationConfig,
>           placeholders: { default: '', emptyDocument: '' },
>         }
>       : {},
>     [collaborationConfig],
>   );
>
>   const refreshVersions = useCallback(async () => {
>     const v = await getVersions();
>     setVersions(v);
>   }, [getVersions]);
>
>   useEffect(() => {
>     if (manager) refreshVersions();
>   }, [manager, refreshVersions]);
>
>   const handleSaveVersion = useCallback(async (e: React.FormEvent) => {
>     e.preventDefault();
>     if (!versionInput.trim()) return;
>     await saveVersion(versionInput.trim());
>     setVersionInput('');
>     await refreshVersions();
>   }, [versionInput, saveVersion, refreshVersions]);
>
>   const handleRestoreVersion = useCallback(async (versionId: string) => {
>     await restoreVersion(versionId);
>     await refreshVersions();
>   }, [restoreVersion, refreshVersions]);
>
>   const getStatusText = () => {
>     if (isLoading) return 'Connecting...';
>     switch (status) {
>       case 'connected': return isSynced ? 'Connected & Synced' : 'Connected, syncing...';
>       case 'connecting': return 'Connecting...';
>       case 'disconnected': return 'Disconnected';
>       default: return status;
>     }
>   };
>
>   return (
>     <div className="editor-container">
>       <div id="editor-header">
>         {veltUser ? `Editing as ${veltUser.name}` : 'Please login'}
>       </div>
>       <div id="status">{getStatusText()}</div>
>       <div id="editor" className="editor-content">
>         {editor && collaborationConfig ? (
>           <BlockNoteView editor={editor} theme="light" />
>         ) : (
>           <div className="editor-loading">Loading editor...</div>
>         )}
>       </div>
>       <div className="versions-section">
>         <form onSubmit={handleSaveVersion}>
>           <input
>             className="version-input"
>             value={versionInput}
>             onChange={(e) => setVersionInput(e.target.value)}
>             placeholder="Version name..."
>           />
>           <button className="save-version-btn" type="submit">Save Version</button>
>         </form>
>         <ul className="version-list">
>           {versions.map((v) => (
>             <li key={v.versionId} className="version-item">
>               <span className="version-name">{v.versionName}</span>
>               <button className="restore-btn" onClick={() => handleRestoreVersion(v.versionId)}>
>                 Restore
>               </button>
>             </li>
>           ))}
>         </ul>
>       </div>
>     </div>
>   );
> };
> ```

> [tab] Other Frameworks
>
> A complete collaborative BlockNote editor with SDK initialization, user login, status display, and version management.
>
> **HTML structure**
>
> ```html theme={null}
> <div class="app-container">
>   <header class="app-header">
>     <h1>BlockNote CRDT Demo</h1>
>     <div id="user-controls"></div>
>   </header>
>
>   <main class="app-content">
>     <div id="editor-header">Please login to start editing</div>
>     <div id="status" class="status-bar">Connecting...</div>
>     <div id="editor" class="editor-content"></div>
>
>     <div class="versions-section">
>       <h3>Versions</h3>
>       <form id="version-form">
>         <input type="text" class="version-input" placeholder="Version name..." />
>         <button type="submit" class="save-version-btn">Save Version</button>
>       </form>
>       <ul class="version-list"></ul>
>     </div>
>   </main>
> </div>
> ```
>
> **velt.ts**
>
> ```ts Complete velt.ts expandable lines theme={null}
> import { initVelt } from '@veltdev/client';
> import type { Velt } from '@veltdev/types';
>
> let client: Velt | null = null;
> let veltInitialized = false;
>
> const veltInitSubscribers = new Map<string, (velt: Velt) => void>();
>
> async function initializeVelt() {
>   client = await initVelt('YOUR_API_KEY');
>
>   client.setDocument('my-document-id', { documentName: 'My Document' });
>
>   client.getVeltInitState().subscribe((isReady) => {
>     veltInitialized = !!isReady;
>     if (isReady && client) {
>       veltInitSubscribers.forEach((callback) => callback(client));
>     }
>   });
> }
>
> export const subscribeToVeltInit = (id: string, callback: (velt: Velt) => void) => {
>   veltInitSubscribers.set(id, callback);
>   if (veltInitialized && client) {
>     callback(client);
>   }
> };
>
> export async function loginWithUser(userId: string, name: string) {
>   await client?.identify({ userId, name });
> }
>
> initializeVelt();
> ```
>
> **editor.ts**
>
> ```ts Complete editor.ts expandable lines theme={null}
> import { BlockNoteEditor } from '@blocknote/core';
> import { createCollaboration } from '@veltdev/blocknote-crdt';
> import type { CollaborationManager } from '@veltdev/blocknote-crdt';
> import type { Velt } from '@veltdev/types';
> import { subscribeToVeltInit } from './velt';
>
> let manager: CollaborationManager | null = null;
> let editor: BlockNoteEditor | null = null;
>
> async function initEditor(veltClient: Velt) {
>   // Create the collaboration manager
>   manager = await createCollaboration({
>     editorId: 'my-document-id',
>     veltClient,
>     initialContent: [
>       { type: 'heading', props: { level: 1 }, content: 'Welcome' },
>       { type: 'paragraph', content: 'Start collaborating here...' },
>     ],
>     onError: (error) => console.error('Collaboration error:', error),
>   });
>
>   // Get the collaboration config for BlockNote
>   const collabConfig = manager.getCollaborationConfig();
>
>   // Create the BlockNote editor with collaboration enabled
>   editor = BlockNoteEditor.create({
>     collaboration: collabConfig,
>     placeholders: { default: '', emptyDocument: '' },
>   });
>
>   // Mount the editor to the DOM
>   editor.mount(document.getElementById('editor'));
>
>   // Wire up status display
>   updateStatus(manager.status, manager.synced);
>   manager.onStatusChange((status) => updateStatus(status, manager!.synced));
>   manager.onSynced((synced) => updateStatus(manager!.status, synced));
> }
>
> function updateStatus(status: string, synced: boolean) {
>   const el = document.getElementById('status');
>   if (!el) return;
>
>   if (status === 'connected') {
>     el.textContent = synced ? 'Connected & Synced' : 'Connected, syncing...';
>   } else if (status === 'connecting') {
>     el.textContent = 'Connecting...';
>   } else {
>     el.textContent = 'Disconnected';
>   }
> }
>
> // Wait for SDK to be ready
> subscribeToVeltInit('editor', (velt) => initEditor(velt));
>
> // Version management
> export async function saveVersion(name: string) {
>   await manager?.saveVersion(name);
> }
>
> export async function getVersions() {
>   return await manager?.getVersions() ?? [];
> }
>
> export async function restoreVersion(versionId: string) {
>   await manager?.restoreVersion(versionId);
> }
> ```

> [tab] Live Demo
>
> [Open in larger window](https://velt-blocknote-crdt-demo.vercel.app/)
>
> [https://velt-blocknote-crdt-demo.vercel.app/](https://velt-blocknote-crdt-demo.vercel.app/)

## How It Works

1. **`useCollaboration` (React) / `createCollaboration` (non-React)** creates a `CollaborationManager`. This initializes a CRDT Store (`type: 'xml'`, content key `'document-store'`), a Yjs `Y.XmlFragment`, and a `SyncProvider`.
2. **The collaboration config** (returned by the hook or `manager.getCollaborationConfig()`) contains the `SyncProvider`, `Y.XmlFragment`, user info, and cursor label settings. This config is passed to `useCreateBlockNote({ collaboration: ... })` (React) or `BlockNoteEditor.create({ collaboration: ... })` (non-React).
3. **User types** -> BlockNote/ProseMirror transaction -> `Y.XmlFragment` mutation -> Yjs CRDT broadcasts via Velt backend -> all connected clients see the change.
4. **Remote cursors** are tracked via Yjs Awareness. BlockNote renders colored cursor labels at each remote user's selection position using its built-in collaboration cursor support.
5. **Undo/redo** automatically switches to Yjs UndoManager when collaboration is enabled — no need to disable any history extension.
6. **Initial content** is applied only once for brand-new documents. The manager waits for remote content before deciding the document is new.
7. **Conflict resolution** is handled by Yjs CRDTs — concurrent typing at different positions merges correctly, and formatting changes on different text ranges are independent.
8. **Version management** saves the full Yjs state as a named snapshot. Restoring a version replaces the current state and broadcasts to all clients.
9. **Cleanup** is automatic — the manager destroys the store, provider, and all listeners when destroyed or the component unmounts.

## APIs

### React: useCollaboration()

The primary React hook for collaborative BlockNote editing. Creates a `CollaborationManager`, initializes the CRDT store, and returns a `BlockNoteCollaborationConfig` with cursor support.

* Signature: `useCollaboration(config: UseCollaborationConfig)`
* Params: [UseCollaborationConfig](https://velt.dev/docs/api-reference/sdk/models/data-models.md#usecollaborationconfig-blocknote)
  * `editorId`: Unique identifier for this collaborative session.
  * `initialContent`: Block content applied once for brand-new documents.
  * `debounceMs`: Throttle interval (ms) for backend writes. Default: `0`.
  * `forceResetInitialContent`: If `true`, always clear and re-apply `initialContent`. Default: `false`.
  * `veltClient`: Explicit Velt client. Falls back to `VeltProvider` context.
  * `showCursorLabels`: Cursor label display mode. Default: `'activity'`.
  * `onError`: Error callback.
* Returns: [UseCollaborationReturn](https://velt.dev/docs/api-reference/sdk/models/data-models.md#usecollaborationreturn-blocknote)
  * `collaborationConfig`: BlockNote collaboration config. `null` while loading.
  * `isLoading`: `true` until CollaborationManager is initialized.
  * `isSynced`: `true` after the initial sync with the backend completes.
  * `status`: Connection status: `'connecting'`, `'connected'`, or `'disconnected'`.
  * `error`: Most recent error, or `null`.
  * `manager`: The underlying `CollaborationManager`. `null` before initialization.
  * `saveVersion`: Save a named version snapshot. Returns the version ID, or empty string on failure.
  * `getVersions`: List all saved versions. Returns empty array on failure.
  * `restoreVersion`: Restore to a saved version. Returns `true` on success, `false` on failure.

```tsx theme={null}
const {
  collaborationConfig,
  isLoading,
  isSynced,
  status,
  error,
  manager,
  saveVersion,
  getVersions,
  restoreVersion,
} = useCollaboration({
  editorId: 'my-blocknote-editor',
  onError: (err) => console.error('Collaboration error:', err),
});
```

### Non-React: createCollaboration()

Factory function that creates a `CollaborationManager`, calls `initialize()`, and returns a ready-to-use instance.

* Signature: `createCollaboration(config: CollaborationConfig): Promise<CollaborationManager>`
* Params: [CollaborationConfig](https://velt.dev/docs/api-reference/sdk/models/data-models.md#collaborationconfig-blocknote)
  * `editorId`: Unique editor/document identifier for syncing.
  * `veltClient`: Velt client instance — must have an authenticated user.
  * `initialContent`: Block content applied once for brand-new documents.
  * `debounceMs`: Throttle interval (ms) for backend writes. Default: `0`.
  * `onError`: Callback for non-fatal errors.
  * `forceResetInitialContent`: If `true`, always reset to `initialContent`. Default: `false`.
* Returns: `Promise<CollaborationManager>`

```js theme={null}
import { createCollaboration } from '@veltdev/blocknote-crdt';

const manager = await createCollaboration({
  editorId: 'my-document-id',
  veltClient: client,
  onError: (error) => console.error(error),
});
```

### CollaborationManager Methods

Once the `manager` is available (non-null from the hook, or returned from `createCollaboration`), you can use its full API:

#### manager.getCollaborationConfig()

Returns the collaboration config object that BlockNote expects. Pass this to `BlockNoteEditor.create({ collaboration: ... })` or `useCreateBlockNote({ collaboration: ... })`.

* Params: `options?: { showCursorLabels?: 'activity' | 'always' }` — Optional cursor display overrides
* Returns: `BlockNoteCollaborationConfig | null`

```js theme={null}
const collabConfig = manager.getCollaborationConfig();

const editor = BlockNoteEditor.create({
  collaboration: collabConfig,
});
```

#### manager.onStatusChange()

Subscribe to connection status changes.

* Signature: `manager.onStatusChange(callback: (status: SyncStatus) => void): Unsubscribe`
* Returns: `Unsubscribe` (call to stop listening)

```js theme={null}
const unsubscribe = manager.onStatusChange((status) => {
  console.log('Connection:', status);
});

unsubscribe(); // Stop listening
```

#### manager.onSynced()

Subscribe to sync state changes.

* Signature: `manager.onSynced(callback: (synced: boolean) => void): Unsubscribe`
* Returns: `Unsubscribe`

```js theme={null}
const unsubscribe = manager.onSynced((synced) => {
  if (synced) console.log('Initial sync complete');
});
```

#### manager.initialized

Whether `initialize()` has completed.

* Returns: `boolean`

#### manager.synced

Whether initial sync with the backend has completed.

* Returns: `boolean`

#### manager.status

Current connection status.

* Returns: `SyncStatus` (`'connecting' | 'connected' | 'disconnected'`)

#### manager.saveVersion()

Save a named snapshot of the current document state.

* Signature: `manager.saveVersion(name: string): Promise<string>`
* Returns: `Promise<string>` (version ID, or empty string on failure)

```js theme={null}
const versionId = await manager.saveVersion('Before major edit');
```

#### manager.getVersions()

List all saved versions for this document.

* Returns: `Promise<Version[]>` (empty array on failure)

```js theme={null}
const versions = await manager.getVersions();
```

#### manager.restoreVersion()

Restore the document to a previously saved version. The restored state is pushed to all connected clients.

* Signature: `manager.restoreVersion(versionId: string): Promise<boolean>`
* Returns: `Promise<boolean>` (`true` on success, `false` on failure)

```js theme={null}
await manager.restoreVersion(versions[0].versionId);
```

#### manager.setStateFromVersion()

Apply a Version object's state locally to the current document.

* Signature: `manager.setStateFromVersion(version: Version): Promise<void>`
* Returns: `Promise<void>`

```js theme={null}
await manager.setStateFromVersion(version);
```

#### manager.getDoc()

Get the underlying Yjs document.

* Returns: `Y.Doc | null`

```js theme={null}
const doc = manager.getDoc();
```

#### manager.getXmlFragment()

Get the XmlFragment bound to BlockNote's `document-store` key.

* Returns: `Y.XmlFragment | null`

```js theme={null}
const xml = manager.getXmlFragment();
```

#### manager.getProvider()

Get the sync provider.

* Returns: `SyncProvider | null`

```js theme={null}
const provider = manager.getProvider();
```

#### manager.getAwareness()

Get the Yjs Awareness instance.

* Returns: `Awareness | null`

```js theme={null}
const awareness = manager.getAwareness();
```

#### manager.getStore()

Get the core CRDT Store.

* Returns: `Store<string> | null`

```js theme={null}
const crdtStore = manager.getStore();
```

#### manager.destroy()

Full cleanup (automatic on editor destroy or component unmount). Safe to call multiple times.

* Returns: `void`

```js theme={null}
manager.destroy();
```

### 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[]`.

> [tab] React / Next.js
>
> ```ts theme={null}
> 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}
> />
> ```

> [tab] Other Frameworks
>
> ```ts theme={null}
> 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()](https://velt.dev/docs/api-reference/sdk/api/api-methods.md#setencryptionprovider-encryptionprovider)
· [VeltEncryptionProvider](https://velt.dev/docs/api-reference/sdk/models/data-models.md#veltencryptionprovider)
· [EncryptConfig](https://velt.dev/docs/api-reference/sdk/models/data-models.md#encryptconfig)
· [DecryptConfig](https://velt.dev/docs/api-reference/sdk/models/data-models.md#decryptconfig)

## Migration Guide: v1 to v2

### React

#### Overview

The v2 API replaces `useVeltBlockNoteCrdtExtension()` with `useCollaboration()`. The new hook provides richer reactive state (status, sync, error), returns version management methods directly, and exposes the `CollaborationManager` for advanced use.

#### Key Changes

| Aspect | v1 (deprecated) | v2 (current) |
| - | - | - |
| Entry point | `useVeltBlockNoteCrdtExtension(config)` | `useCollaboration(config)` |
| Collab config | `response.collaborationConfig` | `response.collaborationConfig` (same usage) |
| Store access | `response.store` (VeltBlockNoteStore) | `response.manager` (CollaborationManager) |
| Version management | Via `store` methods | `saveVersion`, `getVersions`, `restoreVersion` (first-class) |
| Status tracking | Not available | `response.status`, `response.isSynced` |
| Error handling | Not available | `onError` callback + `response.error` state |
| Cursor labels | Not configurable | `showCursorLabels: 'activity' \| 'always'` |
| Cleanup | Automatic on unmount | Automatic on unmount |

#### Step-by-Step

**1. Replace the hook:**

```tsx theme={null}
// Before (v1)
import { useVeltBlockNoteCrdtExtension } from '@veltdev/blocknote-crdt-react';
const { collaborationConfig, isLoading, store } = useVeltBlockNoteCrdtExtension({
  editorId: 'my-doc',
});

// After (v2)
import { useCollaboration } from '@veltdev/blocknote-crdt-react';
const { collaborationConfig, isLoading, isSynced, status, error, manager, saveVersion, getVersions, restoreVersion } = useCollaboration({
  editorId: 'my-doc',
  onError: (err) => console.error(err),
});
```

**2. Editor creation (same pattern):**

```tsx theme={null}
const editor = useCreateBlockNote(
  collaborationConfig ? { collaboration: collaborationConfig } : {},
  [collaborationConfig],
);
```

**3. Use version management (new in v2):**

```tsx theme={null}
await saveVersion('Draft v1');
const versions = await getVersions();
await restoreVersion(versions[0].versionId);
```

**4. Add status monitoring (new in v2):**

```tsx theme={null}
if (error) return <div>Error: {error.message}</div>;
<div>Status: {status} | Synced: {isSynced ? 'Yes' : 'No'}</div>
```

### Non-React

#### Overview

The non-React v2 API provides `createCollaboration()` from `@veltdev/blocknote-crdt`, replacing any previous callback-based patterns. The returned `CollaborationManager` provides `getCollaborationConfig()` for BlockNote editor creation.

#### Step-by-Step

**1. Create the manager:**

```js theme={null}
import { createCollaboration } from '@veltdev/blocknote-crdt';
const manager = await createCollaboration({ editorId: 'my-doc', veltClient: client });
```

**2. Create the editor:**

```js theme={null}
const collabConfig = manager.getCollaborationConfig();
const editor = BlockNoteEditor.create({ collaboration: collabConfig });
editor.mount(document.getElementById('editor'));
```

**3. Monitor status:**

```js theme={null}
manager.onStatusChange((status) => console.log('Status:', status));
manager.onSynced((synced) => console.log('Synced:', synced));
```

**4. Version management:**

```js theme={null}
await manager.saveVersion('Draft v1');
const versions = await manager.getVersions();
await manager.restoreVersion(versionId);
```

**5. Cleanup:**

```js theme={null}
manager.destroy();
```

## Legacy API (v1)

> [!WARNING]
>
> The v1 API is deprecated. Use the v2 `useCollaboration` hook (React) or `createCollaboration` (non-React) for all new integrations. The v1 API is still exported for backward compatibility.

### React: useVeltBlockNoteCrdtExtension() (deprecated)

A React hook that returns a collaboration config and store for BlockNote integration. Internally delegates to `useCollaboration` (v2) via a compatibility wrapper.

* Signature: `useVeltBlockNoteCrdtExtension(config: VeltBlockNoteCrdtExtensionConfig)`
* Params: [VeltBlockNoteCrdtExtensionConfig](https://velt.dev/docs/api-reference/sdk/models/data-models.md#veltblocknotecrdtextensionconfig-deprecated)
* Returns: [VeltBlockNoteCrdtExtensionResponse](https://velt.dev/docs/api-reference/sdk/models/data-models.md#veltblocknotecrdtextensionresponse-deprecated)

```tsx theme={null}
import { useVeltBlockNoteCrdtExtension } from '@veltdev/blocknote-crdt-react';

const { collaborationConfig, isLoading } = useVeltBlockNoteCrdtExtension({
  editorId: 'my-document-id',
  initialContent: JSON.stringify([{ type: 'paragraph', content: '' }]),
});

const editor = useCreateBlockNote({
  collaboration: collaborationConfig,
}, [collaborationConfig]);

return (
  <BlockNoteView
    editor={editor}
    key={collaborationConfig ? 'collab-on' : 'collab-off'}
  />
);
```

### React: VeltBlockNoteCrdtExtensionConfig (deprecated)

| Property | Type | Required | Description |
| - | - | - | - |
| `editorId` | `string` | Yes | Unique editor identifier. |
| `initialContent` | `string` | No | JSON string of blocks for new documents. |
| `debounceMs` | `number` | No | Throttle interval (ms). |

### React: VeltBlockNoteCrdtExtensionResponse (deprecated)

| Property | Type | Description |
| - | - | - |
| `collaborationConfig` | `{ fragment: any; provider: any; user: { name: string; color: string } } \| null` | Config to pass to `useCreateBlockNote`. |
| `store` | `VeltBlockNoteStore \| null` | Store instance for version management. |
| `isLoading` | `boolean` | `true` until the store is ready. |

### VeltBlockNoteStore Methods (deprecated)

| Method | Returns | Description |
| - | - | - |
| `getStore()` | `Store<string>` | Get the core CRDT Store. |
| `getYXml()` | `Y.XmlFragment \| null` | Get the Yjs XmlFragment. |
| `getYDoc()` | `Y.Doc` | Get the Yjs Doc. |
| `isConnected()` | `boolean` | Check if connected to backend. |
| `setStateFromVersion(v)` | `Promise<void>` | Apply a version's state. |
| `destroy()` | `void` | Clean up all resources. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.