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.
git clone https://github.com/dream-num/univer-collaboration-examples.gitcd univer-collaboration-examplespnpm install --frozen-lockfilepnpm example:quick-startOpen:
http://127.0.0.1:3010/?unit=quick-start-sheet&type=2Verify realtime collaboration
- Open the full URL in one browser window.
- Open it again in another browser profile or private window.
- Change a cell in either Sheet.
- 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
Univer Collaboration Client → Node Transport → UniverCollabEndpoint → UniverCollabService → MemoryDatabaseAdapterTwo-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:
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 rxjsThe 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:
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:
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:
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:
/?unit=quick-start-sheet&type=25. Replace teaching configuration with application capabilities
| Teaching configuration | Replace it with |
|---|---|
Fixed demo-user | Application authentication Middleware and a stable user ID |
| Permission checks always allow | Server ACL covering read, JOIN, submit, and lifecycle operations |
| Fixed Unit | Application create API and product record calling createUnitFromData() |
| Memory Adapter | SQLite 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
| Symptom | Check |
|---|---|
| Install or build fails | Node.js 24+, the declared pnpm version, and matching Univer package versions |
| Port 3010 is occupied | Stop the previous example, or run with PORT=3011 and use that port in both browser URLs |
| Empty page or missing assets | Build completed; run start from the example directory; #app has height |
| Page opens but edits do not sync | Same complete URL; successful snapshot and session-ticket requests; WebSocket connection at /universer-api/comb/connect |
| Data disappears after restart | Quick Start uses memory; continue with the SQLite example |
How is this guide?