# How to Implement Real-Time Collaboration Using Yjs with reactjs-tiptap-editor

> Learn to implement real time collaboration with Yjs in your reactjs-tiptap-editor. Integrate Yjs for seamless multi-user editing and synchronization.

- Repository: [Hung Hoang/reactjs-tiptap-editor](https://github.com/hunghg255/reactjs-tiptap-editor)
- Tags: how-to-guide
- Published: 2026-03-03

---

**Add the Yjs dependencies already present in the package, instantiate a `HocuspocusProvider` with a `Y.Doc`, configure the `@tiptap/extension-collaboration` and `@tiptap/extension-collaboration-caret` extensions, and wrap the editor in `RichTextProvider` to enable real-time synchronization.**

The `reactjs-tiptap-editor` repository provides a complete rich-text editing solution built on Tiptap and ProseMirror. By leveraging the **Conflict-free Replicated Data Type (CRDT)** capabilities of Yjs, you can transform this standalone editor into a fully collaborative environment where multiple users edit the same document simultaneously with automatic conflict resolution.

## Prerequisites and Dependencies

The repository already includes all necessary packages for real-time collaboration using Yjs in its [`package.json`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/package.json). You do not need to install additional dependencies:

```json
{
  "yjs": "^13.6.28",
  "y-protocols": "^1.0.7",
  "@tiptap/extension-collaboration": "^3.14.0",
  "@tiptap/extension-collaboration-caret": "^3.14.0",
  "@hocuspocus/provider": "^3.4.3"
}

```

*Source:* [[`package.json`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/package.json) lines 887–889](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/package.json#L887-L889)

While `@hocuspocus/provider` is the default WebSocket transport, you can substitute it with `y-webrtc` or a custom Yjs provider without changing the extension configuration logic.

## Setting Up the Yjs Document and Hocuspocus Provider

Create a `Y.Doc` instance and bind it to a provider before initializing the editor. In [`playground/src/App.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/playground/src/App.tsx), import the required modules:

```tsx
import * as Y from 'yjs';
import { HocuspocusProvider } from '@hocuspocus/provider';
import Collaboration from '@tiptap/extension-collaboration';
import CollaborationCaret from '@tiptap/extension-collaboration-caret';

```

*Source:* [[`playground/src/App.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/playground/src/App.tsx) lines 9–13](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/playground/src/App.tsx#L9-L13)

Instantiate the document and provider outside the component or in a stable scope:

```tsx
const ydoc = new Y.Doc();

const provider = new HocuspocusProvider({
  url: 'wss://your-hocuspocus-server.com',
  name: 'document-room-id',
  document: ydoc,
});

```

*Source:* [[`playground/src/App.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/playground/src/App.tsx) lines 99–105](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/playground/src/App.tsx#L99-L105)

The `name` parameter identifies the collaboration room, while `document` binds the provider to the specific `Y.Doc` instance that stores the shared document state.

## Configuring the Collaboration Extensions

Add the collaboration extensions to your editor's extension array after the base kit. Pass the `Y.Doc` to `Collaboration` and the provider to `CollaborationCaret`:

```tsx
const extensions = [
  ...BaseKit,
  
  Collaboration.configure({
    document: ydoc,
  }),
  
  CollaborationCaret.configure({
    provider,
    user: {
      name: 'Current User',
      color: '#ff6600',
    },
  }),
];

```

*Source:* Based on the commented block at [[`playground/src/App.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/playground/src/App.tsx) lines 332–340](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/playground/src/App.tsx#L332-L340)

**`Collaboration.configure()`** accepts the `Y.Doc` instance and replaces the default history extension with Yjs's native undo/redo stack. **`CollaborationCaret.configure()`** requires the provider to access awareness data, which tracks cursor positions and user selections across connected clients.

## Managing Awareness and Cleanup

To monitor connected users, subscribe to the provider's awareness state:

```tsx
provider.awareness.on('change', ({ added, updated, removed }) => {
  const users = provider.awareness.getStates();
  console.log('Active users:', users.size);
});

```

Prevent memory leaks by disconnecting the provider when the component unmounts:

```tsx
useEffect(() => {
  return () => provider.disconnect();
}, []);

```

The `RichTextProvider` component from `reactjs-tiptap-editor` handles the editor context for toolbars and bubble menus without requiring additional configuration for collaborative features.

## Complete Working Implementation

Below is a minimal, production-ready example combining all steps:

```tsx
// playground/src/App.tsx
import { useEffect } from 'react';
import { useEditor, EditorContent } from '@tiptap/react';
import { Document, Text, Paragraph } from '@tiptap/extensions';
import * as Y from 'yjs';
import { HocuspocusProvider } from '@hocuspocus/provider';
import Collaboration from '@tiptap/extension-collaboration';
import CollaborationCaret from '@tiptap/extension-collaboration-caret';
import { RichTextProvider } from 'reactjs-tiptap-editor';
import 'reactjs-tiptap-editor/style.css';

// 1. Initialize Yjs and Provider
const ydoc = new Y.Doc();
const provider = new HocuspocusProvider({
  url: 'wss://your-hocuspocus-endpoint',
  name: 'shared-doc-001',
  document: ydoc,
});

// 2. Define base extensions
const BaseKit = [Document, Text, Paragraph];

export default function App() {
  // 3. Create editor with collaboration
  const editor = useEditor({
    extensions: [
      ...BaseKit,
      Collaboration.configure({ document: ydoc }),
      CollaborationCaret.configure({
        provider,
        user: { name: 'User A', color: '#2563eb' },
      }),
    ],
  });

  // 4. Cleanup on unmount
  useEffect(() => {
    return () => provider.disconnect();
  }, []);

  // 5. Render with RichTextProvider
  return (
    <RichTextProvider editor={editor}>
      <EditorContent editor={editor} />
    </RichTextProvider>
  );
}

```

Open this component in multiple browser instances to observe real-time text synchronization and remote cursor visualization.

## Key Implementation Files

Understanding these source files ensures proper integration:

- **[`package.json`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/package.json)**: Declares Yjs, Tiptap collaboration extensions, and the Hocuspocus provider dependencies.
- **[`playground/src/App.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/playground/src/App.tsx)**: Contains the commented reference implementation demonstrating provider instantiation and extension configuration.
- **[`src/components/RichTextProvider.tsx`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/components/RichTextProvider.tsx)**: Wraps the editor instance to supply context for UI components; remains unchanged when adding collaboration.
- **[`src/store/editor.ts`](https://github.com/hunghg255/reactjs-tiptap-editor/blob/main/src/store/editor.ts)**: Provides the `useTiptapEditor` hook consumed by the UI layer.

## Best Practices for Production

Follow these guidelines when deploying real-time collaboration using Yjs:

- **Unique Room Identification**: Use distinct `name` values (e.g., `doc-${uuid}`) to isolate different documents.
- **Authentication**: Pass auth tokens via `provider.setAuth({ token })` before connecting to secure the WebSocket endpoint.
- **User Identity**: Generate stable user IDs to prevent awareness data collisions; randomize colors per session for visual distinction.
- **Garbage Collection**: Enable `ydoc.gc = true` for long-running documents to prune deleted content and reduce memory usage.
- **Provider Alternatives**: Substitute Hocuspocus with `y-webrtc` for peer-to-peer scenarios or a custom WebSocket server implementing the Yjs sync protocol.

## Summary

- The `reactjs-tiptap-editor` repository includes `yjs`, `@tiptap/extension-collaboration`, and `@hocuspocus/provider` by default.
- Instantiate a `Y.Doc` and `HocuspocusProvider` with your WebSocket endpoint before creating the editor instance.
- Configure `Collaboration` with the `Y.Doc` and `CollaborationCaret` with the provider to enable synchronization and cursor awareness.
- Wrap the editor in `RichTextProvider` to maintain toolbar functionality; no internal changes to the provider are required.
- Disconnect the provider in a cleanup effect to prevent memory leaks and orphaned WebSocket connections.

## Frequently Asked Questions

### What is the difference between the Collaboration and CollaborationCaret extensions?

**`@tiptap/extension-collaboration`** handles document state synchronization by binding the ProseMirror editor to a `Y.Doc`, ensuring all clients share the same content. **`@tiptap/extension-collaboration-caret`** provides the visual layer, reading awareness data from the provider to render remote users' cursors and selections with distinct colors.

### Can I use a different provider instead of Hocuspocus?

Yes. Any Yjs-compatible provider works, including `y-webrtc` for peer-to-peer connections or a custom WebSocket server. Replace the `HocuspocusProvider` import and instantiation while keeping the same `Y.Doc` reference passed to `Collaboration.configure()`.

### How do I handle authentication for the collaboration server?

Configure authentication through the provider options before connecting. For Hocuspocus, use `provider.setAuth({ token: 'your-jwt' })` or pass an `onAuthentication` callback in the constructor. Secure the WebSocket endpoint server-side to validate tokens before allowing document sync.

### Why do I need to disable the default History extension when using collaboration?

The standard Tiptap `History` extension manages undo/redo through ProseMirror's native mechanism, which conflicts with Yjs's CRDT-based history. When you add `Collaboration`, it automatically replaces the native history with Yjs's shared undo manager, ensuring all clients maintain a consistent operation history across the collaborative session.