The @veltdev/blocknote-crdt-react library enables real-time collaborative editing on BlockNote 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:
npm install @veltdev/blocknote-crdt-react

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 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.
const { collaborationConfig } = useVeltBlockNoteCrdtExtension({
  editorId: 'YOUR_EDITOR_ID',
  initialContent: JSON.stringify([{ type: "paragraph", content: "" }])
});

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

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

Notes

  • Unique editorId: Use a unique editorId per editor instance.
  • Pass collaborationConfig: Provide an editor and key with collaborationConfig to BlockNote.

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.

Complete Example

See Velt BlockNote Demo Repo for our complete implementation.
Relevant Code
import '@blocknote/core/fonts/inter.css';
import { BlockNoteView } from "@blocknote/mantine";
import '@blocknote/mantine/style.css';
import { useCreateBlockNote } from "@blocknote/react";
import { useVeltBlockNoteCrdtExtension } from "@veltdev/blocknote-crdt-react";
import React from "react";

const BlockNoteCollaborativeEditor: React.FC = () => {

  const { collaborationConfig, isLoading } = useVeltBlockNoteCrdtExtension({
    editorId: 'YOUR_EDITOR_ID',
    initialContent: JSON.stringify([{ type: "paragraph", content: "" }])
  });

  // Now call the hook at top level with the config
  const editor = useCreateBlockNote({
    collaboration: collaborationConfig,
    // Add any other static options here
  }, [collaborationConfig]); // Deps ensure re-init if config changes

  return (
    <>
      <div className="editor-container">
        <div className="editor-content">
          <BlockNoteView editor={editor} key={collaborationConfig ? 'collab-on' : 'collab-off'} />
        </div>
        <div className="status">
          {!isLoading ? 'Connected to collaborative session' : 'Connecting to collaborative session...'}
        </div>
      </div>
    </>
  );
};

export default BlockNoteCollaborativeEditor;

APIs

Custom Encryption

You can 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[].
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}
/>

See also: setEncryptionProvider() · VeltEncryptionProvider · EncryptConfig · DecryptConfig

useVeltBlockNoteCrdtExtension()

Provides real-time collaborative BlockNote editor functionality with Yjs and Velt SDK synchronization.
  • Signature: useVeltBlockNoteCrdtExtension(config: VeltBlockNoteCrdtExtensionConfig)
  • Params: VeltBlockNoteCrdtExtensionConfig
    • editorId: Unique identifier for the editor instance.
    • initialContent: Initial content for the editor.
    • debounceMs: Debounce time for update propagation (ms).
  • Returns: VeltBlockNoteCrdtExtensionResponse
    • collaborationConfig: Configuration object to pass into BlockNote.
    • store: BlockNote CRDT store instance.
    • isLoading: Whether the store is initialized. false once store is initialized, true by default.

Store Methods

store.getStore()

Access the underlying CRDT store managing document state.
  • Returns: Store
const crdtStore = store.getStore();

store.destroy()

Tear down the store and clean up listeners/resources.
  • Returns: void
store.destroy();

store.getYDoc()

Accessor for the underlying Yjs document.
  • Returns: Y.Doc
const ydoc = store.getYDoc();

store.getYXml()

Returns the Y.XmlFragment instance that handles Tiptap’s rich text structure.
  • Returns: Y.XmlFragment | null
const yxml = store.getYXml();