Headless Univer

Headless Univer is Univer’s content runtime without an interactive editor UI. It shares content models, commands, calculation, and Facade APIs with the browser editor. It can process content in Node.js or run UI-free tasks in a browser. Available capabilities depend on the registered product and feature plugins.

In Node.js, you can create or load documents, read and edit paragraphs and text, change structure and styles, and save Univer snapshots. Tables, lists, columns, formulas, and shapes require their content plugins. Content edits do not produce page screenshots.

The example below uses plugin mode. @univerjs/core and product content plugins belong to the Headless content layer; @univerjs/ui, product *-ui packages, and default browser core presets do not belong in this configuration. The example retains @univerjs/engine-render as a shared dependency without mounting a Canvas or editor. See Headless package boundaries for the classification and exceptions.

Local snapshot operations need no collaboration service. The collaboration example additionally needs a client, server, and authentication; Office conversion and screenshots require Server SDK and a rendering runtime, respectively. Importing /facade alone neither registers feature plugins nor makes UI APIs Headless APIs.

Installation

Shell
pnpm add @univerjs/core @univerjs/engine-render @univerjs/engine-formula @univerjs/docs

Usage

TypeScript
import { LocaleType, Univer } from '@univerjs/core'import { FUniver } from '@univerjs/core/facade'import { UniverDocsPlugin } from '@univerjs/docs'import { UniverFormulaEnginePlugin } from '@univerjs/engine-formula'import { UniverRenderEnginePlugin } from '@univerjs/engine-render'import '@univerjs/docs/facade'const univer = new Univer({  locale: LocaleType.EN_US,  locales: {    [LocaleType.EN_US]: {},  },})univer.registerPlugin(UniverRenderEnginePlugin)univer.registerPlugin(UniverFormulaEnginePlugin)univer.registerPlugin(UniverDocsPlugin)const univerAPI = FUniver.newAPI(univer)univerAPI.createDocument({})

Collaborative editing in Node.js

Node.js can act as a collaboration client: load an existing document from the server, modify it through the Facade API, and synchronize changes to other clients with the same document open. Before running the example, prepare a collaboration service, a client license, and an accessible document ID.

Example files

Organize the files as follows:

text
src/docs/node/├── main.ts└── univer.ts
  • univer.ts: Create Univer and register document feature plugins, Facade extensions, and the Node.js collaboration client.
  • main.ts: Load a collaborative document and demonstrate paragraphs, text, headers and footers, callouts, code blocks, quotes, lists, shapes, tables, and columns.

Replace your-license, the server URL, and the Cookie in univer.ts, as well as the document ID in main.ts.

TypeScript
import { UniverCollaborationPlugin } from '@univerjs-pro/collaboration'import { UniverCollaborationClientPlugin } from '@univerjs-pro/collaboration-client'import {  NodeCollaborationSocketService,  UniverCollaborationClientNodePlugin,} from '@univerjs-pro/collaboration-client-node'import { UniverDocsCalloutPlugin } from '@univerjs-pro/docs-callout'import { UniverDocsCodePlugin } from '@univerjs-pro/docs-code'import { UniverDocsColumnPlugin } from '@univerjs-pro/docs-column'import { UniverDocsLatexPlugin } from '@univerjs-pro/docs-latex'import { UniverDocsListPlugin } from '@univerjs-pro/docs-list'import { UniverDocsQuotePlugin } from '@univerjs-pro/docs-quote'import { UniverDocsReferencePlugin } from '@univerjs-pro/docs-reference'import { UniverDocsShapePlugin } from '@univerjs-pro/docs-shape'import { UniverDocsTablePlugin } from '@univerjs-pro/docs-table'import { UniverLicensePlugin } from '@univerjs-pro/license'import {  IAuthzIoService,  IImageIoService,  IMentionIOService,  IUndoRedoService,  LocaleType,  Univer,} from '@univerjs/core'import { UniverDocsPlugin } from '@univerjs/docs'import { UniverDocsDrawingPlugin } from '@univerjs/docs-drawing'import { UniverDocsHyperLinkPlugin } from '@univerjs/docs-hyper-link'import { UniverDrawingPlugin } from '@univerjs/drawing'import { UniverRenderEnginePlugin } from '@univerjs/engine-render'import { UniverNetworkPlugin } from '@univerjs/network'import { UniverThreadCommentPlugin } from '@univerjs/thread-comment'import '@univerjs/docs/facade'import '@univerjs/docs-drawing/facade'import '@univerjs-pro/docs-shape/facade'import '@univerjs-pro/docs-callout/facade'import '@univerjs-pro/docs-reference/facade'import '@univerjs-pro/docs-code/facade'import '@univerjs-pro/docs-list/facade'import '@univerjs-pro/docs-quote/facade'import '@univerjs-pro/docs-table/facade'import '@univerjs-pro/docs-column/facade'import '@univerjs-pro/docs-latex/facade'import '@univerjs-pro/collaboration-client/facade'export function createUniverOnNode(): Univer {  const univer = new Univer({    locale: LocaleType.EN_US,    locales: {},    override: [      [IAuthzIoService, null],      [IUndoRedoService, null],      [IMentionIOService, null],    ],  })  univer.registerPlugin(UniverLicensePlugin, {    license: 'your-license',  })  univer.registerPlugin(UniverRenderEnginePlugin)  univer.registerPlugin(UniverThreadCommentPlugin)  univer.registerPlugin(UniverDrawingPlugin, {    override: [[IImageIoService, null]],  })  univer.registerPlugin(UniverDocsPlugin)  univer.registerPlugin(UniverDocsDrawingPlugin)  univer.registerPlugin(UniverDocsHyperLinkPlugin)  univer.registerPlugin(UniverDocsReferencePlugin)  univer.registerPlugin(UniverDocsCalloutPlugin)  univer.registerPlugin(UniverDocsCodePlugin)  univer.registerPlugin(UniverDocsListPlugin)  univer.registerPlugin(UniverDocsQuotePlugin)  univer.registerPlugin(UniverDocsShapePlugin)  univer.registerPlugin(UniverDocsTablePlugin)  univer.registerPlugin(UniverDocsColumnPlugin)  univer.registerPlugin(UniverDocsLatexPlugin)  registerCollaborationPlugins(univer)  return univer}function registerCollaborationPlugins(univer: Univer): void {  const serverUrl = 'https://your-univer-server.example.com'  univer.registerPlugin(UniverNetworkPlugin)  univer.registerPlugin(UniverCollaborationPlugin)  univer.registerPlugin(UniverCollaborationClientPlugin, {    socketService: NodeCollaborationSocketService,    enableOfflineEditing: false,    enableSingleActiveInstanceLock: false,    snapshotServerUrl: `${serverUrl}/universer-api/snapshot`,    collabSubmitChangesetUrl: `${serverUrl}/universer-api/comb`,    collabWebSocketUrl: `${serverUrl}/universer-api/comb/connect`,    wsSessionTicketUrl: `${serverUrl}/universer-api/user/session-ticket`,    uploadFileServerUrl: `${serverUrl}/universer-api/stream/file/upload`,    signUrlServerUrl: `${serverUrl}/universer-api/file/{fileID}/sign-url`,    downloadEndpointUrl: serverUrl,    authzUrl: `${serverUrl}/universer-api/authz`,    sendChangesetTimeout: 200,    retryConnectingInterval: 1000,    customHeaders: {      // Example: If your Univer server requires authentication via cookies, you can set the 'cookie' header here.      cookie: 'your-cookie',      // ... more headers    },  })  univer.registerPlugin(UniverCollaborationClientNodePlugin)}

Runtime notes

loadDocAsync() loads an existing server document; you do not need to call createDocument(). The collaboration service identifies the user and checks permissions through authentication information such as the Cookie in customHeaders. The server returns the connection’s memberID.

This example disables browser offline caching and the single-active-instance lock across tabs, so there is no need to call setCurrentUser() for cache isolation between accounts. If you later use features such as comments that depend on the local current user, you can set the application user through UserManagerService; this does not replace server authentication.

The header/footer example applies only to paged documents, while the column example applies only to modern documents. The example checks the document mode with isModern() and skips unsupported operations.

The example inserts content into an existing document, so use a test document. The example keeps the collaboration connection open after running. Open the same document in a browser to see the changes. flush() waits for server acknowledgement of queued collaboration changes. For a one-shot script, call univer.dispose() after all asynchronous operations and synchronization have finished.

How is this guide?

© 2026 DreamNum Co., Ltd.