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

Before running the example

This repository requires Node.js 24+ and declares pnpm 10.32.1. Check node --version and pnpm --version, use the repository lockfile, and run all pnpm example:* commands from the repository root. These commands build the browser and server, type-check the example, then start it; they are not a hot-reload development server. After editing source, stop the process and run the command again.

If you have a client license, set UNIVER_LICENSE in your shell before building. The example’s vite.config.ts embeds it into the browser bundle. Setting it only when starting an already-built server will not update the browser. See client license configuration.

The SQLite examples build better-sqlite3 during installation. Ensure node-gyp, Python and a C/C++ toolchain are available. If installation reports node-gyp: command not found, install node-gyp into your development toolchain and rerun installation with the same Node.js version you will use to start the examples.

Bash
git clone https://github.com/dream-num/univer-collaboration-examples.gitcd univer-collaboration-examplespnpm install --frozen-lockfilepnpm 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 shows the minimal Server and Web assembly. The official Quick Start provides the complete application structure:

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.

Read and migrate the runnable files

Copy examples/quick-start as the initial application skeleton, retaining package.json, index.html, vite.config.ts, web/styles.css, web/main.ts, and server/main.ts. Run its build and start scripts from that directory: Express serves dist/web relative to the working directory. Keep the Vite license definition and give #app a nonzero height. The code below explains the assembly; use the repository files for the complete runnable project.

The example serves the page and /universer-api from the same origin. When moving the frontend elsewhere, proxy both HTTP and the WebSocket upgrade to the backend, or change all five client URLs together. Keep unit equal to the ID created by the backend; type=2 selects Sheets. A URL parameter does not create a document.

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@1.0.0 \  @univerjs-pro/collaboration-client@1.0.0 \  @univerjs-pro/collaboration-client-ui@1.0.0 \  @univerjs-pro/collaboration-database-memory@1.0.0 \  @univerjs-pro/collaboration-endpoint@1.0.0 \  @univerjs-pro/collaboration-service@1.0.0 \  @univerjs-pro/collaboration-transport-node@1.0.0 \  @univerjs-pro/license@1.0.0 \  @univerjs/core@1.0.0 \  @univerjs/preset-sheets-core@1.0.0 \  @univerjs/presets@1.0.0 \  @univerjs/protocol@1.0.0 \  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 type { IWorkbookData } from "@univerjs/core";import { LocaleType } 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 for that route and attaches Transport to the application's HTTP server:

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(express.static("dist/web"));const server = createServer(app);transport.attach(server);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 Web SDK 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.

If the first run fails

SymptomCheck
Install or build failsNode.js 24+, the declared pnpm version, and matching Univer package versions
Port 3010 is occupiedStop the previous example, or run with PORT=3011 and use that port in both browser URLs
Empty page or missing assetsBuild completed; run start from the example directory; #app has height
Page opens but edits do not syncSame complete URL; successful snapshot and session-ticket requests; WebSocket connection at /universer-api/comb/connect
Data disappears after restartQuick Start uses memory; continue with the SQLite example

How is this guide?

© 2026 DreamNum Co., Ltd.