BetterOffice

Collaboration

Connect editors through a shared transport and deploy a collaboration relay

BetterOffice editors synchronize through a Yrs CRDT. Each editor exposes its replica to a collaboration provider, which exchanges updates over a transport you supply. A relay routes those updates between peers in the same room.

How updates flow

  1. The editor publishes its replica through collaboration.onReplica.
  2. The format provider converts replica updates to Yjs sync-v1 frames.
  3. Your transport moves those frames between peers in the same document room.

Use a distinct room ID for every document and format. Updates from different documents or formats are not interoperable.

Deploy the Cloudflare relay

The reference relay uses one Durable Object per room. It accepts WebSockets at /room/:roomId, replays retained frames to new peers, and broadcasts new binary frames. Its wrangler.jsonc contains the required ROOMS binding and SQLite migration.

From a BetterOffice checkout:

bun install
bun run --filter @betteroffice/collaboration-relay dev
bun run --filter @betteroffice/collaboration-relay deploy

Point your browser transport at the deployed Worker origin. The complete demo adapter is createRoomTransport; it handles WebSocket reconnects, binary ownership, and backpressure. If you write your own adapter, implement the CollaborationTransport exported by the format package. A false return from send means the adapter must emit drain when it can accept another frame.

Connect an editor

Import CollaborationProvider from the package for your format:

FormatProvider import
DOCX@betteroffice/docx/collaboration
XLSX@betteroffice/xlsx/collaboration
PPTX@betteroffice/pptx
VSDX (source preview)@betteroffice/vsdx

VSDX packages are available from source.

Create the provider when the editor publishes its replica:

const onReplica = (replica) => {
  provider?.destroy();
  transport?.disconnect();
  if (!replica) return;

  transport = createRoomTransport(relayOrigin, roomId);
  provider = new CollaborationProvider(replica, transport);
  provider.connect();
};

Pass the collaboration options to your editor:

const collaboration = { clientId, initialUpdate, onReplica };

<DocxEditor documentBuffer={docxBytes} collaboration={collaboration} />;
<XlsxEditor file={xlsxBytes} collaboration={collaboration} />;
<PptxEditor file={pptxBytes} fonts={fonts} collaboration={collaboration} />;
<VsdxEditor file={vsdxBytes} fonts={fonts} collaboration={collaboration} />;

clientId must be unique per open peer. initialUpdate must represent the shared starting state for that document. The demo generates these states with build-collaboration-seeds.ts.

Call provider.destroy() and disconnect the transport when the editor unmounts or changes rooms.

Production setup

Before deploying, add authentication and room authorization, set connection and storage quotas, and plan how to snapshot or compact long-lived documents. The reference relay retains at most 512 frames or 16 MiB per room.

On this page