Engineering

Real-time DOCX collaboration with React, Vue 3, and Yjs

Connect React or Vue 3 DOCX editors through WebRTC, Hocuspocus, or Yjs. Synchronize documents, presence, comments, and tracked changes.

3 min read

@docx-editor.dev/pro connects React and Vue 3 DOCX editors through Yjs. Each participant edits a local document replica. A provider moves updates between the replicas. Choose Web Real-Time Communication (WebRTC), Hocuspocus, or a custom Yjs provider.

The collaboration module replicates the complete DOCX package. This includes text, formatting, tables, headers, footers, notes, drawings, relationships, and embedded parts. It also synchronizes participant presence and remote selections. When you enable review features, comments, tracked changes, and review decisions stay synchronized.

Install collaboration for React

Install the editor, Pro package, Yjs, and the WebRTC provider:

npm install @docx-editor.dev/core @docx-editor.dev/react \
  @docx-editor.dev/pro yjs y-webrtc

Vue 3 applications use @docx-editor.dev/vue instead of @docx-editor.dev/react.

Collaboration is part of @docx-editor.dev/pro. You can evaluate it under the EigenPal Pro License. Production use requires a commercial agreement. See the pricing page for available plans.

Connect a WebRTC room

Create one room ID and share it with each participant. Keep the room ID in application state or in the URL. Do not create a new ID during each render.

Create the room ID with createCollaborationRoomId:

import {
  createCollaborationRoomId,
} from '@docx-editor.dev/pro/collaboration/webrtc';
 
const roomId = createCollaborationRoomId();

The roomId identifies the signaling topic. It is not an authorization token. For encrypted signaling, put a shared secret in the URL fragment as #collab=<secret>. The WebRTC helper uses that fragment as its password. Do not use the room ID as the password.

This React component creates or joins a room:

import { DocxEditor } from '@docx-editor.dev/react';
import { useWebrtcCollaboration } from '@docx-editor.dev/pro/react/webrtc';
 
interface CollaborativeEditorProps {
  roomId: string;
  bytes: Uint8Array;
  actorId: string;
  name: string;
}
 
export function CollaborativeEditor({
  roomId,
  bytes,
  actorId,
  name,
}: CollaborativeEditorProps) {
  const { document, modules, session, pending, error } =
    useWebrtcCollaboration({
      room: {
        roomId,
        identity: { actorId, name },
        bootstrap: { kind: 'create-or-join', document: bytes },
      },
    });
 
  if (error) return <p>{error.detail ?? error.code}</p>;
  if (pending || !document) return <p>Connecting…</p>;
 
  return (
    <DocxEditor
      key={session?.sessionId}
      document={document}
      modules={modules}
    />
  );
}

Each participant must use a unique actorId. To prevent concurrent seeding in this basic setup, open the first editor before the second editor. If two isolated peers seed the same room simultaneously, every replica reports concurrent-seed. To recover, create a new room from saved bytes.

Vue 3 provides the same composable at @docx-editor.dev/pro/vue/webrtc. For a complete Vue 3 component, see Connect with useWebrtcCollaboration.

Choose a provider

Use the provider that matches your deployment:

  • WebRTC connects small peer rooms directly. Production deployments need private signaling and Traversal Using Relays around NAT (TURN) servers.
  • Hocuspocus provides server-backed rooms, authentication, and persistence.
  • A custom Yjs provider connects an existing Yjs deployment through createDocumentCollaboration or useDocumentCollaboration.

The WebRTC helper uses a public signaling service when you omit signaling. Use this service only for development. Configure your own signaling URLs and TURN servers for production.

If you use Hocuspocus, import useHocuspocusCollaboration from @docx-editor.dev/pro/react/hocuspocus or @docx-editor.dev/pro/vue/hocuspocus.

Handle room state

Always render error, even after document becomes available. A successful initial connection does not prevent a later authentication, schema, resource, or replication failure.

An error state is terminal for that replica. Save its current bytes and call rejoin(bytes) to join the room again. The room copy wins after a successful rejoin, so the operation can drop changes that never reached the room.

Editing pauses during a transport disconnection by default. If participants must edit while disconnected, set offlineEditing: true. The session merges those edits after reconnection. Continue to show the connection state because the edits have not reached other participants yet.

Treat the room as the document

The room is authoritative while collaboration is active. A saved .docx is a snapshot of the room at one moment. The API does not merge an externally edited file back into the same room.

When collaboration ends, export the room and make that file authoritative again. To use an externally edited file, create a new room from those bytes and retire the old room.

Server code can export synchronized room state with readCollaborationDocument(ydoc). DocxEditor.createCollaborative attaches an editing API replica without a browser.

Watch room growth

A Yjs room grows as edits add records and tombstones. Deleted media also stays in shared state. Use session.resourceUsage() to monitor the room and archive it before it reaches a resource limit.

The default seed limit is 20 MB. One embedded file can use up to 32 mebibytes (MiB), and all embedded files can use up to 64 MiB. The collaboration reference documents the complete limits and typed failure codes.

Next steps

.docx in. .docx out.

Open a real document in the editor, then wire it into your app. It all runs client-side.