Skip to content
2 min

Quick start

Run two-browser collaboration, then move the minimal Client, Transport, Endpoint, Service, and Adapter composition into your application.

This page has two paths. First, run the official example and verify the complete collaboration path in two browsers. Then read and copy the minimal Server and Web composition into your own Univer application.

Before starting, complete the environment and version requirements. Every @univerjs/* and @univerjs-pro/* package must be pinned to the same matching release cohort.

Path one: run the official example

Bash
git clone https://github.com/dream-num/univer-collaboration-examples.git
cd univer-collaboration-examples
pnpm install
pnpm example:quick-start

Open:

Text
http://127.0.0.1:3010/?unit=quick-start-sheet&type=2

Verify realtime collaboration

  1. Open the full URL in one browser window.
  2. Open it again in another browser profile or private window.
  3. Change a cell in either Sheet.
  4. Confirm that the same edit appears in the other browser.

Use two independent browser contexts so the server creates two online Sessions. Watching an edit in the same page is not enough to verify the Room, ACK, and broadcast path.

What success proves

Text
Univer Collaboration Client
  → Node Transport
  → UniverCollabEndpoint
  → UniverCollabService
  → MemoryDatabaseAdapter

Two-browser synchronization means the browsers loaded a Unit over HTTP, used a one-time Session Ticket to open WebSocket Sessions, and joined the same Room. The Service performed OT, confirmed a new revision, saved it through the Adapter, and the Endpoint broadcast it to other members.

Path two: move it into your application

The following code comes from the two official Quick Start entry points, with page-only status text removed. The complete runnable source is available here:

If you already have a Univer Web application, keep its Runtime, presets, and UI, and add only the Collaboration Client configuration and Server below. For an empty project, copy index.html, vite.config.ts, and the styles from the Quick Start directory as the application shell.

1. Install one matching package cohort

The Server needs Transport, Endpoint, Service, and one Database Adapter. The browser needs the Collaboration Client. Install the required packages, then pin every Univer package to the same exact version:

Bash
pnpm add \
  @univerjs-pro/collaboration \
  @univerjs-pro/collaboration-client \
  @univerjs-pro/collaboration-client-ui \
  @univerjs-pro/collaboration-database-memory \
  @univerjs-pro/collaboration-endpoint \
  @univerjs-pro/collaboration-service \
  @univerjs-pro/collaboration-transport-node \
  @univerjs-pro/license \
  @univerjs/core \
  @univerjs/preset-sheets-core \
  @univerjs/presets \
  @univerjs/protocol \
  express react react-dom rxjs

The official example manifest is the runnable source of truth for the current package composition and exact versions.

2. Create the Service, Endpoint, and initial Unit

UniverCollabService is the authoritative collaboration core. The Endpoint maps the browser protocol to the Service, while the Memory Adapter temporarily stores snapshots, changesets, and revisions:

TypeScript
import { createServer } from "node:http";
import express from "express";
import { LocaleType, type IWorkbookData } from "@univerjs/core";
import { MemoryDatabaseAdapter } from "@univerjs-pro/collaboration-database-memory";
import { UniverCollabEndpoint } from "@univerjs-pro/collaboration-endpoint";
import { UniverCollabService } from "@univerjs-pro/collaboration-service";
import { createNodeTransport } from "@univerjs-pro/collaboration-transport-node";
import { ErrorCode, UniverType } from "@univerjs/protocol";

const UNIT_ID = "quick-start-sheet";
const unitData: IWorkbookData = {
  id: UNIT_ID,
  rev: 1,
  name: "Quick Start Sheet",
  appVersion: "",
  locale: LocaleType.EN_US,
  sheetOrder: ["sheet-1"],
  sheets: {
    "sheet-1": {
      id: "sheet-1",
      name: "Sheet 1",
      rowCount: 100,
      columnCount: 26,
      cellData: {},
    },
  },
  styles: {},
  resources: [],
};

const database = new MemoryDatabaseAdapter();
const service = new UniverCollabService({ dbAdapter: database });
const endpoint = new UniverCollabEndpoint(service);
const transport = createNodeTransport();

transport.use(async (context, next) => {
  context.userID = "demo-user";
  await next();
});
transport.register(endpoint);

await service.createUnitFromData(
  { type: UniverType.UNIVER_SHEET, data: unitData },
  { userID: "demo-user" },
);

The fixed demo-user demonstrates how trusted identity flows from Transport into Endpoint and Service. A production application validates its own cookie, bearer token, or session inside transport.use() before setting a stable business userID.

3. Mount authorization, HTTP, and WebSocket entry points

The current Collaboration Client queries an authorization protocol. Quick Start always returns allowed: true, then forwards /universer-api HTTP requests and WebSocket upgrades to Transport:

TypeScript
const app = express();

app.post("/universer-api/authz/-/object/-/batch_allowed", express.json(), (request, response) => {
  const body = request.body as {
    requests: Array<{ unitID: string; objectID: string; actions: unknown[] }>;
  };
  response.json({
    error: { code: ErrorCode.OK, message: "" },
    objectActions: body.requests.map((item) => ({
      unitID: item.unitID,
      objectID: item.objectID,
      actions: item.actions.map((action) => ({ action, allowed: true })),
    })),
  });
});

app.use("/universer-api", (request, response) => {
  request.url = request.originalUrl;
  transport.handleRequest(request, response);
});
app.use(express.static("dist/web"));

const server = createServer(app);
server.on("upgrade", (request, socket, head) => {
  transport.handleUpgrade(request, socket, head);
});
server.listen(3010, "127.0.0.1");

Always-allow is a teaching shortcut, not a security boundary. A real application must protect HTTP reads, realtime JOIN, changeset submission, and Unit lifecycle operations. See Identity and authorization for the complete coverage model.

4. Register the Collaboration Client in browser Runtime

Keep the normal Univer Runtime and presets, then add the Collaboration plugins and protocol URLs:

TypeScript
import { LocaleType, LogLevel } from "@univerjs/core";
import { UniverCollaborationPlugin } from "@univerjs-pro/collaboration";
import { UniverCollaborationClientPlugin } from "@univerjs-pro/collaboration-client";
import CollaborationClientEnUS from "@univerjs-pro/collaboration-client/locale/en-US";
import {
  BrowserCollaborationSocketService,
  UniverCollaborationClientUIPlugin,
} from "@univerjs-pro/collaboration-client-ui";
import CollaborationClientUIEnUS from "@univerjs-pro/collaboration-client-ui/locale/en-US";
import { UniverLicensePlugin } from "@univerjs-pro/license";
import { UniverSheetsCorePreset } from "@univerjs/preset-sheets-core";
import UniverPresetSheetsCoreEnUS from "@univerjs/preset-sheets-core/locales/en-US";
import { createUniver, defaultTheme, mergeLocales } from "@univerjs/presets";
import "@univerjs/preset-sheets-core/lib/index.css";
import "@univerjs-pro/collaboration-client-ui/lib/index.css";

const httpProtocol = location.protocol === "https:" ? "https" : "http";
const wsProtocol = location.protocol === "https:" ? "wss" : "ws";
const baseURL = `${httpProtocol}://${location.host}/universer-api`;

createUniver({
  locale: LocaleType.EN_US,
  locales: {
    [LocaleType.EN_US]: mergeLocales(
      UniverPresetSheetsCoreEnUS,
      CollaborationClientEnUS,
      CollaborationClientUIEnUS,
    ),
  },
  theme: defaultTheme,
  logLevel: LogLevel.WARN,
  collaboration: true,
  presets: [UniverSheetsCorePreset({ container: "app" })],
  plugins: [
    [UniverLicensePlugin, { license: import.meta.env.UNIVER_LICENSE || undefined }],
    UniverCollaborationPlugin,
    [
      UniverCollaborationClientPlugin,
      {
        socketService: BrowserCollaborationSocketService,
        sendChangesetTimeout: 200,
        authzUrl: `${baseURL}/authz`,
        snapshotServerUrl: `${baseURL}/snapshot`,
        collabSubmitChangesetUrl: `${baseURL}/comb`,
        collabWebSocketUrl: `${wsProtocol}://${location.host}/universer-api/comb/connect`,
        wsSessionTicketUrl: `${baseURL}/user/session-ticket`,
      },
    ],
    UniverCollaborationClientUIPlugin,
  ],
});

The page needs an element with id="app" and selects the Unit through query parameters:

Text
/?unit=quick-start-sheet&type=2

5. Replace teaching configuration with application capabilities

Teaching configurationReplace it with
Fixed demo-userApplication authentication Middleware and a stable user ID
Permission checks always allowServer ACL covering read, JOIN, submit, and lifecycle operations
Fixed UnitApplication create API and product record calling createUnitFromData()
Memory AdapterSQLite or a contract-tested custom persistent Adapter

Next, complete Database Adapters and Identity and authorization. Then add History, Thread Comment, Worktree, or Exchange as needed. See the examples index for every runnable composition.