> ## 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.

# Tiptap Editor

> Setup Multiplayer Editing for Tiptap Editor.

The `@veltdev/tiptap-crdt-react` library enables real-time collaborative editing on Tiptap 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:

<Tabs>
  <Tab title="React / Next.js">
    ```bash theme={null}
    npm install @veltdev/tiptap-crdt-react @tiptap/react @tiptap/starter-kit @tiptap/extension-collaboration @tiptap/extension-collaboration-cursor
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```bash theme={null}
    npm install @veltdev/tiptap-crdt @veltdev/client @tiptap/core @tiptap/starter-kit @tiptap/extension-collaboration-caret
    ```
  </Tab>
</Tabs>

### Step 2: Setup Velt

Initialize the Velt client by following the [Velt Setup Docs](/get-started/quickstart). 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.

<Tabs>
  <Tab title="React / Next.js">
    ```jsx theme={null}
    const { VeltCrdt } = useVeltTiptapCrdtExtension({
      editorId: 'YOUR_EDITOR_ID'
    });

    const editor = useEditor({
      extensions: [
        StarterKit.configure({
          history: false,
        }),
        ...(VeltCrdt ? [VeltCrdt] : []),
      ],
      content: ''
    }, [VeltCrdt]);

    return <EditorContent editor={editor} />;
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```js theme={null}
    // 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: '',
    });
    ```
  </Tab>
</Tabs>

### Step 4: Add CSS for Collaboration Cursor

* Add the following CSS to your app to style the collaboration cursor.

```css theme={null}
/* Collaboration cursor styling */
.collaboration-cursor__caret,
.collaboration-carets__caret {
  border-left: 1px solid #0d0d0d;
  border-right: 1px solid #0d0d0d;
  margin-left: -1px;
  margin-right: -1px;
  pointer-events: none;
  position: relative;
  word-break: normal;
}

/* Render the username above the caret */
.collaboration-cursor__label,
.collaboration-carets__label {
  border-radius: 3px 3px 3px 0;
  color: #0d0d0d;
  font-size: 12px;
  font-style: normal;
  font-weight: 600;
  left: -1px;
  line-height: normal;
  padding: 0.1rem 0.3rem;
  position: absolute;
  top: -1.4em;
  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:**

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.
* Desynced content: Make sure Tiptap `history` is disabled when using collaboration.

<Tip>
  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](/realtime-collaboration/crdt/setup/core#veltcrdtstoremap) to inspect and monitor your CRDT stores in real-time.
</Tip>

## Complete Example

<Tabs>
  <Tab title="React / Next.js">
    [Open Live Demo In Larger Window](https://sample-apps-tiptap-crdt-demo.vercel.app/) | [View Demo Repo](https://github.com/velt-js/sample-apps/tree/main/apps/react/crdt/text-editors/tiptap/tiptap-crdt-demo)

    ```jsx Complete Implementation expandable lines theme={null}
    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;
    ```
  </Tab>

  <Tab title="Other Frameworks">
    [Open Live Demo In Larger Window](https://sample-apps-tiptap-non-react-crdt-d.vercel.app) | [View Demo Repo](https://github.com/velt-js/sample-apps/tree/main/apps/javascript/crdt/text-editors/tiptap/tiptap-crdt-demo)

    ```js Complete Implementation expandable lines theme={null}
    // 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();
    ```
  </Tab>

  <Tab title="React / Next.js Live Demo">
    [Open Live Demo In Larger Window](https://sample-apps-tiptap-crdt-demo.vercel.app/) | [View Demo Repo](https://github.com/velt-js/sample-apps/tree/main/apps/react/crdt/text-editors/tiptap/tiptap-crdt-demo)

    <Frame>
      <iframe src="https://sample-apps-tiptap-crdt-demo.vercel.app/" className="w-full" height="500px" />
    </Frame>
  </Tab>

  <Tab title="Non-React Demo">
    [Open Live Demo In Larger Window](https://sample-apps-tiptap-non-react-crdt-d.vercel.app/) | [View Demo Repo](https://github.com/velt-js/sample-apps/tree/main/apps/javascript/crdt/text-editors/tiptap/tiptap-crdt-demo)

    <Frame>
      <iframe src="https://sample-apps-tiptap-non-react-crdt-d.vercel.app/" className="w-full" height="500px" />
    </Frame>
  </Tab>
</Tabs>

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

<Tabs>
  <Tab title="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>

  <Tab title="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);
    ```
  </Tab>
</Tabs>

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

### Initialization

<Tabs>
  <Tab title="React / Next.js">
    #### [useVeltTiptapCrdtExtension()](/api-reference/sdk/api/api-methods#usevelttiptapcrdtextension)

    React hook that provides real-time collaborative editing on Tiptap editor with Yjs and Velt SDK.

    * Signature: `useVeltTiptapCrdtExtension(config: VeltTiptapCrdtExtensionConfig)`
    * Params: [VeltTiptapCrdtExtensionConfig](/api-reference/sdk/models/data-models#velttiptapcrdtextensionconfig)
      * `editorId`: Unique identifier for the editor instance.
      * `initialContent`: Initial content for the editor.
      * `debounceMs`: Debounce time for update propagation (ms).
    * Returns: [VeltTiptapCrdtExtensionResponse](/api-reference/sdk/models/data-models#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.

    ```jsx theme={null}
    const { VeltCrdt, store, isLoading } = useVeltTiptapCrdtExtension({ 
      editorId: 'velt-tiptap-crdt-demo' 
    });
    ```
  </Tab>

  <Tab title="Other Frameworks">
    #### [createVeltTipTapStore()](/api-reference/sdk/api/api-methods#createvelttiptapstore)

    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.

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

    const store = await createVeltTipTapStore({
      editorId: 'velt-tiptap-crdt-demo',
      veltClient: client,
      initialContent: '<p>Start collaborating...</p>',
    });
    ```

    #### [store.getCollabExtension()](/api-reference/sdk/api/api-methods#getcollabextension)

    Get the Tiptap collaboration extension bound to the current Yjs document.

    * Returns: `Extension`

    ```js theme={null}
    const collabExtension = store.getCollabExtension();
    ```
  </Tab>
</Tabs>

### Store Methods

#### [store.getStore()](/api-reference/sdk/api/api-methods#getstore)

Access the underlying CRDT store managing document state.

* Returns: Store

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

#### [store.destroy()](/api-reference/sdk/api/api-methods#destroy)

Tear down the store and clean up listeners/resources.

* Returns: void

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

#### [store.getYDoc()](/api-reference/sdk/api/api-methods#getydoc)

Accessor for the underlying Yjs document.

* Returns: Y.Doc

```jsx theme={null}
const ydoc = store.getYDoc();
```

#### [store.getYXml()](/api-reference/sdk/api/api-methods#getyxml)

Returns the Y.XmlFragment instance that handles Tiptap's rich text structure.

* Returns: Y.XmlFragment | null

```jsx theme={null}
const yxml = store.getYXml();
```

#### [store.setStateFromVersion()](/api-reference/sdk/api/api-methods#setstatefromversion)

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>`

```js theme={null}
// Restore document to a previous version
await store.setStateFromVersion(previousVersion);
```

{/* #### [createVeltTipTapStore](/api-reference/sdk/api/api-methods#createvelttiptapstore)

Create and initialize a collaborative Tiptap store instance.

- Params: [VeltTipTapStoreConfig](/api-reference/sdk/models/data-models#velttiptapstoreconfig)
- Returns: [VeltTipTapStore](/api-reference/sdk/models/data-models#velttiptapstore)

<Tabs>
<Tab title="React / Next.js">
```jsx
const store = await createVeltTipTapStore({ editorId: 'velt-tiptap-crdt-demo' });
```
</Tab>

<Tab title="Other Frameworks">
```js
const store = await createVeltTipTapStore({ editorId: 'velt-tiptap-crdt-demo' });
```
</Tab>
</Tabs>

#### [initialize()](/api-reference/sdk/api/api-methods#initialize)

Initialize the store and start syncing the collaborative document.

- Returns: void

<Tabs>
<Tab title="React / Next.js">
```jsx
await store.initialize();
```
</Tab>

<Tab title="Other Frameworks">
```js
await store.initialize();
```
</Tab>
</Tabs>

#### [getCollabExtension()](/api-reference/sdk/api/api-methods#getcollabextension)

Get the Tiptap collaboration extension bound to the current Yjs document.

- Returns: Extension

<Tabs>
<Tab title="React / Next.js">
```jsx
const collab = store.getCollabExtension();
```
</Tab>

<Tab title="Other Frameworks">
```js
const collab = store.getCollabExtension();
```
</Tab>
</Tabs>
*/}
