Spire.OfficeJs (18)
Co-editing lets multiple users open the same document at the same time, with everyone's changes visible to everyone else, without sending files back and forth or merging manually. Spire.OfficeJS has co-editing built in: with the right page configuration, two browser tabs or two users can join the same editing session. This article uses Word as an example. It first explains how co-editing works and what it requires, then walks through the key configuration piece by piece, and finally gives a complete runnable example.
How Co-editing Works
The following diagram and steps explain how co-editing works in Spire.OfficeJS.

- User 1 and User 2 open the document with the same co-editing identifier (that is, the same
fileInfo.primary). - User 1 edits the document.
- The editor reports the changes to the co-authoring service (the service that
coAuthorUrlpoints to). - The co-authoring service distributes the changes to User 2's editor.
- User 2's editor applies the changes, so the same content appears on both screens.
The key point: the same co-editing session must use the same primary. In addition, you need to connect to an Office Server (see Prerequisites for Co-editing); everything else is configuration and state management built around these two things.
Many people assume when they first encounter online editing that the browser edits the Word file on the server directly. That is not the case. Spire.OfficeJS edits an internal cached editing file (Editor.bin), not the original Word file:

Prerequisites for Co-editing
Co-editing does not "just work" once you embed the editor in a page. For two users to join the same co-editing session and see each other's changes, two conditions must both hold:
- The same primary:
fileInfo.primaryis not an ordinary "file ID" but the identity of the current co-editing version. Make surefileInfo.primaryis the same (for how it works and how it is generated, see The Co-editing Identifier: fileInfo.primary). - Connect to an Office Server: the editor needs to connect to an Office Server (either locally started or remotely deployed) and use
coAuthorUrlto specify its address (see The Co-authoring Service Address: coAuthorUrl).
Both are required: with different primary values you get two separate sessions. Without a co-authoring service, changes have no channel to be distributed through.
The simplest way to verify: open the page in two tabs using the same fileInfo.primary but different user.id values. As long as primary matches, they join the same co-editing session — effectively "co-editing with yourself".
Tip
If you want some people to take part in co-editing in read-only mode, set their editorAttrs.canEdit to false. In fast mode they can still see other people's changes in real time but cannot edit, which suits observation, demonstration, or approval scenarios.
The Co-editing Identifier: fileInfo.primary
fileInfo.primary is the sole basis on which Spire.OfficeJS decides "is this the same document being co-edited?" All participants must use the same value to join the same co-editing session.
Generation and Reuse
- It must be supplied explicitly by the page and stay stable for the same session. If left empty, Spire.OfficeJS generates a random one, and each user gets a different value every time they open the page, so the sessions end up independent and unsynchronized.
- Character set: a randomly generated value consists of lowercase letters and digits and is about 20 characters long.
- Do not reuse: one "editing session" of a document corresponds to one
primary; a new round of editing should use a new value.
After a save completes, it is recommended to use a new primary the next time the document is opened: this isolates the "saved content" from "a new round of co-editing" and keeps the cache, version, and new changes of the previous session from mixing together. If you keep reusing an old value for repeated editing, content rollbacks or synchronization anomalies are easy to run into.
When you rotate primary, also point sourceUrl at the file saved in this round (usually the file address obtained from onSave). Otherwise, the first open with the new primary re-downloads and converts the old original file, and the content you just saved is lost.
Fast Mode and Strict Mode
Spire.OfficeJS provides two co-editing modes: fast mode and strict mode. You switch between them in the UI via "Review → Editing Mode" or "File → Advanced Settings → Co-editing Mode". Both let multiple people join the same session; the difference is when changes are synchronized and who triggers the save: fast mode syncs as you edit, while strict mode lets everyone edit their own copy and merges only on save.
Fast Mode (Default)
In fast mode, anyone's changes are broadcast to all participants in real time: text, cursor position, and selections are immediately visible with no need to save. Before editing a region (a paragraph, a cell, etc.), the editor first acquires a block lock on that region; others see the region as occupied until editing finishes or the lock is released.

Strict Mode
In strict mode, everyone edits in their own isolated copy but still belongs to the same co-editing session: other people's changes stay hidden until saved, and regions edited by multiple people at the same time are marked with dashed lines in different colors. Only after you click "Save" is the whole document submitted to the co-authoring service for merging.

Comparison of the Two Modes
| Dimension | Fast Mode (Default) | Strict Mode |
|---|---|---|
| Change visibility | Broadcast in real time; other people's cursors and changes are immediately visible | Isolated; neither side sees the other's changes before saving |
| Saving | Automatic sync (no manual save needed) | Syncs only after you click "Save" manually |
| Undo / Redo | Unavailable | Available |
| Notification of others' changes | None (already seen in real time) | Click Save and reload |
| Suitable for | Multiple people making small, high-frequency collaborative edits | Large edits, isolated review, concerns about accidental overwrites |
Configuring the Default Mode
Controlled by two switches under user.customization.public.common:
user: {
customization: {
public: {
common: {
CollaborationModeIsFast: true, // Enter fast mode on open
CollaborationModeOnlyFast: false
}
}
}
}
CollaborationModeIsFast: the initial mode when the document opens.truemeans fast mode,falsemeans strict mode. Fast mode is the default.CollaborationModeOnlyFast: when set totrue, locks fast mode; the strict option in "Co-editing Mode" is disabled and users cannot switch to strict mode. Suits scenarios that require everyone to always share changes in real time.
The Co-authoring Service Address: coAuthorUrl
coAuthorUrl points to the editor service that provides co-authoring capability (the Office Server) and must be configured explicitly. It is configured under editorAttrs.serverless; an example:
editorAttrs: {
useWebAssemblyDoc: true,
serverless: {
useServerless: true,
baseUrl: 'https://LocalExample:3000', // Address of the front-end resources (web directory)
coAuthorUrl: 'https://LocalExample:8000' // Co-authoring service (Office Server) address
}
}
Note the distinction between the three kinds of addresses: the load address of SpireCloudEditor.js and baseUrl only determine where the front-end resources are loaded from; they are not the co-authoring service address. coAuthorUrl must point to an Office Server reachable by the browser; otherwise co-editing cannot work either.
Quick Start: Two People Editing the Same Document
This section assembles the preceding content into a minimal runnable page: just replace the script address, service address, document address, user information, and save handling.
Page Structure
Place a container div on the page, then include the Spire.OfficeJS editor script:
<div id="editor"></div>
<!-- The editor script is served by your Spire.OfficeJS service -->
<script src="https://LocalExample:3000/web/editors/spireapi/SpireCloudEditor.js"></script>
Initialization Configuration
<script>
// Co-editing session primary: same document, same co-editing session — must be identical for all users
const DOC_PRIMARY = 'a1b2c3d4e5f6g7h8i9j0';
// Current user: id must be unique and stable; name is the display name during collaboration
const currentUser = {
id: 'user-1001',
name: 'John Doe'
};
const config = {
fileAttrs: {
fileInfo: {
name: 'project-plan.docx',
ext: 'docx',
primary: DOC_PRIMARY // Co-editing session primary
},
sourceUrl: 'https://your-file-host/files/project-plan.docx'
},
user: {
id: currentUser.id,
name: currentUser.name,
customization: {
public: {
common: {
CollaborationModeIsFast: true, // Enter fast mode on open
CollaborationModeOnlyFast: false
}
}
}
},
editorAttrs: {
useWebAssemblyDoc: true,
serverless: {
useServerless: true,
baseUrl: 'https://LocalExample:3000', // Address of the front-end resources (web directory)
coAuthorUrl: 'https://LocalExample:8000' // Co-authoring service (Office Server) address
},
editorMode: 'edit',
editorType: 'document',
editorWidth: '100%',
editorHeight: '800px',
viewLanguage: 'zh',
canEdit: true,
canReview: true,
canComment: true,
canChat: true,
events: {
// Handle the save in the page: event.data[1] is the File object of the current document
onSave: function (event) {
const fileName = event.data[0];
const file = event.data[1];
const form = new FormData();
form.append('file', file, fileName);
form.append('primary', DOC_PRIMARY);
fetch('https://your-file-host/save-file', {
method: 'POST',
body: form
});
}
}
}
};
new SpireCloudEditor.OpenApi('editor', config);
</script>
FAQ
Both Users Open the Page but Changes Do Not Sync
Troubleshoot in this order:
- Whether
fileAttrs.fileInfo.primaryis the same. Differentprimaryvalues lead to two independent sessions; even ifsourceUrlis exactly the same, they will not sync. - Whether an Office Server is configured and connected.
editorAttrs.serverless.coAuthorUrlmust point to an Office Server reachable by the browser: either started locally (for examplehttp://127.0.0.1:8000) or deployed remotely. If it is missing or misconfigured, co-editing fails (see The Co-authoring Service Address: coAuthorUrl). sourceUrlonly takes effect on the first open or a cache miss. Once the sameprimaryhits the cache, it is no longer read. To switch files, updatesourceUrland use a newprimaryat the same time.
The "Co-editing Mode" Entry Is Missing
The entry requires editorMode: 'edit'.
Cannot Undo / Redo in Fast Mode
This is by design in fast mode: changes are broadcast in real time and synced automatically, so they cannot be rolled back from a single client. If you need an undo history, switch to strict mode, edit in the isolated copy, and then save.
Co-editing Breaks or Content Rolls Back After Saving
It is most likely that an old primary was reused. After a save completes (onSave has received the file), the next time you open the document you should use a new primary and point sourceUrl at the saved file; do not keep using the old value of a completed save for a new session.
On a business page, users open the editor to edit a document and then need to close it. Many pages simply remove the editor from the page when the user closes it. The editor is no longer visible, but some of its listeners are still running in the background. If the editor is opened and closed repeatedly, the old listeners may still respond to operations on the new document, and the page becomes slower. When the editor is no longer needed, clean it up with destroyEditor(). This article uses Word as an example to explain how to destroy a Spire.OfficeJS editor.
Destroy process
Destroying the editor can be summarized in three steps: open the editor, call destroyEditor(), and restore the page to its original state.

- The user opens the editor.
OpenApicreates the editor on the business page, and the original placeholder<div>is replaced. - Call
destroyEditor()to remove the editor. It unbinds themessagelistener and themouseuplistener on the business page, then replaces the editor iframe with the original placeholder<div>. - The page returns to its state before opening, and the placeholder
<div>appears again.
How to call it
destroyEditor() is not a global method, nor is it on the editor iframe element. It is a method on the object returned by new SpireCloudEditor.OpenApi(). Save the returned object when you create the editor, and call the method on that object when you destroy the editor.
Example code:
var editor = new SpireCloudEditor.OpenApi('editor', config);
editor.destroyEditor();
editor = null;
Note
Call destroyEditor() only once per editor instance; calling it again on the same instance throws an error. After a successful destroy, set editor to null to avoid using the stale object. To open the editor again, create a new instance.
Complete example
Below is a complete example: the page creates the editor and saves the returned object when the page loads, then calls destroyEditor() when the user clicks the "Destroy Editor" button. Replace LocalExample:3000 with the actual deployment address before running.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Spire.OfficeJS Destroy Editor</title>
<style>
html, body { height: 100%; margin: 0; background-color: #cbe4f3;}
body { display: flex; flex-direction: column; }
#editor { flex: 1; min-height: 0; }
#editor iframe { width: 100%; height: 100%; border: 0; }
</style>
</head>
<body>
<button id="destroyBtn">Destroy Editor</button>
<div id="editor"></div>
<script src="http://LocalExample:3000/web/editors/spireapi/SpireCloudEditor.js"></script>
<script>
var SERVER = 'http://LocalExample:3000';
var config = {
fileAttrs: {
fileInfo: { name: 'sample.docx', ext: 'docx' },
sourceUrl: SERVER + '/samples/sample.docx'
},
user: { id: 'uid-1', name: 'Jonn' },
editorAttrs: {
editorMode: 'edit',
editorType: 'document',
viewLanguage: 'en',
useWebAssemblyDoc: true,
spireOfficeJsLicense: '',
serverless: {
useServerless: true,
baseUrl: SERVER
}
}
};
var editor = new SpireCloudEditor.OpenApi('editor', config);
document.getElementById('destroyBtn').addEventListener('click', function () {
editor.destroyEditor();
editor = null;
});
</script>
</body>
</html>
After you open the document and click the "Destroy Editor" button, the editor is destroyed:

Note
destroyEditor() does not save the document and does not cancel save or download requests that have already been sent. If you need to keep changes, save first and wait for the save to complete before destroying the editor.
FAQ
The container style changes after destroying the editor
Cause: The placeholder <div> restored by destroyEditor() has only its id set; its original class and inline style are not restored.
Solution: Put the container's size and styles in an ID selector, for example #editor { flex: 1; min-height: 0; }.
Can I still use methods such as downloadAs after calling destroyEditor()?
Cause: destroyEditor() unbinds the listeners on the business page, so the returned object no longer receives responses from the editor.
Solution: Do not use other methods on the returned object after calling destroyEditor().
Does destroyEditor() clear localStorage or the server-side cache?
Cause: destroyEditor() only performs cleanup on the business page; it does not clear browser storage or the server-side cache.
Solution: If cleanup is needed, handle it separately in the business system.
When the editor is integrated into a business system, its UI usually needs to follow the product. Some products do not want users to see the vendor branding; some pages already have a header, so the editor's title bar looks redundant; sometimes the editor only occupies a narrow side panel where the full toolbar does not fit; and some products want documents to open at a suitable zoom level. All of these can be configured through customization, without changing the editor itself. The examples below use Word. The table below summarizes the Spire.OfficeJS UI settings; each is explained in the following sections.
| Setting | Purpose | Default |
|---|---|---|
whiteLabel |
Remove the vendor logo and branding text | false |
hideTitle |
Hide the top title bar | false |
hideStatusBar |
Hide the bottom status bar | false |
hideRuler |
Hide the ruler | false |
defToolbar |
Toolbar mode | full |
defaultZoom |
Zoom level at open | 1 |
zoomFit |
Fit width or page at open | None |
White-labeling
whiteLabel removes vendor branding from the editor UI. When enabled, the vendor logo on the right side of the title bar is hidden and the "File → About Us" entry disappears. Other menus, the toolbar, and document content are unaffected.
common: {
whiteLabel: true
}
The figure below shows the before and after.

Hide UI regions
The title bar, status bar, and ruler sit at the top, bottom, and above the document page. When space is tight, hide any of them as needed; the three settings are independent.
Hide the title bar
hideTitle hides the title bar at the top of the editor and gives the document more room. Use it when the surrounding page already has its own header.
common: {
header: {
hideTitle: true
}
}
The figure below shows the before and after.

Hide the status bar
hideStatusBar hides the status bar at the bottom of the editor. The status bar shows page numbers, word count, zoom, and similar information; hiding it gives the document more height.
common: {
hideStatusBar: true
}
The figure below shows the before and after.

Hide the ruler
The ruler is a quick way to adjust indents and margins. hideRuler controls whether it is shown in WordJS and PresentationJS. The ruler is visible by default; set it to true to hide it.
common: {
hideRuler: true
}
The figure below shows the before and after.

Note
These options only set the initial state when the document opens. Users can still show the title bar, status bar, or ruler again from the toolbar; they will be hidden again the next time the document opens.
Toolbar mode
defToolbar sets the toolbar style used when a document opens.
| Value | Description |
|---|---|
full |
Full toolbar (default), commands grouped by tab |
short |
Compact toolbar, buttons condensed into one row for narrow containers |
common: {
header: {
defToolbar: 'short'
}
}
The figure below compares the two modes.

Zoom
A document can open at a fixed zoom level, or automatically fit the editor width or the full page. The first uses defaultZoom; the second uses zoomFit.
Default zoom
defaultZoom sets the zoom level used when a document opens. 1 means 100% and 1.25 means 125%.
common: {
defaultZoom: 1.25
}
The figure below shows the status bar at 125%.

Zoom range
The supported range is 0.5–5. Values outside the range are clamped to 0.5 or 5, and a notice appears on the page.
Fit width / fit page
zoomFit makes the document fit the editor width or the full page when it opens. width fits the width; page fits the whole page.
| Value | Description |
|---|---|
width |
Fit width: the page width fills the editor |
page |
Fit page: the whole page is visible in the editor |
common: {
zoomFit: 'width'
}
The figure below compares fit width and fit page.

Complete example
The configuration above, combined, is shown below:
<div id="editor"></div>
<script src="http://LocalExample:3000/web/editors/spireapi/SpireCloudEditor.js"></script>
<script>
var SERVER = 'http://LocalExample:3000';
var config = {
fileAttrs: {
fileInfo: { name: 'sample.docx', ext: 'docx' },
sourceUrl: SERVER + '/samples/sample.docx'
},
user: {
id: 'uid-1',
name: 'Jonn',
canSave: true,
customization: {
public: {
common: {
whiteLabel: true,
defaultZoom: 1.25, // fixed zoom; use zoomFit instead to fit the container
// zoomFit: 'width', // width = fit width, page = fit page
hideStatusBar: true,
hideRuler: true,
header: {
hideTitle: true,
defToolbar: 'short'
}
}
}
}
},
editorAttrs: {
editorMode: 'edit',
editorType: 'document',
viewLanguage: 'en',
useWebAssemblyDoc: true,
serverless: {
useServerless: true,
baseUrl: SERVER
}
}
};
new SpireCloudEditor.OpenApi('editor', config);
</script>
FAQ
The UI does not change after editing the configuration
Cause: The browser may have cached the old page. If you use the bundled example system, it reads its configuration at startup, so changes require a restart.
Solution: Force-refresh the page first. With the example system, restart the service and reopen the page.
hideTitle or defToolbar has no effect
Cause: hideTitle and defToolbar are title-bar settings and must be placed under common.header; placed directly under common, they are ignored.
Solution: Use the structure user.customization.public.common.header.hideTitle and user.customization.public.common.header.defToolbar.
White-labeling is on but the red notice or watermark remains
Cause: whiteLabel only removes vendor branding from the UI and does not handle the warning watermark. Without a valid License, the document still shows the trial notice and restricts content.
Solution: Contact sales@e-iceblue.com to purchase a License or apply for a temporary License, then fill in the License Key for the product in editorAttrs, for example spireDocJsLicense for Word.
Spire.OfficeJS is an online Office document editing suite that supports private deployment. It is also an embeddable online Office editor for developers and enterprises that need to integrate document editing into their own web applications. Users can open, view, edit, and save Word, Excel, and PowerPoint documents directly in the browser, and preview PDFs in read-only mode. No Microsoft Office, WPS, or browser plug-in is required on the client.
Rather than fragmented, low-level APIs, Spire.OfficeJS provides a JavaScript Office editor that can be embedded directly into business pages. The page loads SpireCloudEditor.js, prepares a container element, and passes in the configuration; the editor then loads the corresponding interface as an iframe. Document parsing, layout, rendering, and editing are handled by default by the in-browser WebAssembly document engine. This does not mean there is nothing to deploy, however: SpireCloudEditor.js, the editor UI, the WebAssembly engine, and resources such as fonts must still be hosted on a resource service that the browser can reach. For the full product composition and runtime architecture, see Product Composition and Runtime Architecture.
Key Features
- Online editing: Online editing for Word, Excel, and PowerPoint documents; open, view, edit, and save documents in the browser, with "Download as" export.
- PDF preview: Read-only PDF preview within the same interface.
- Collaboration and review: Supports collaborative editing, comments, revisions, and document history.
- UI customization: Supports appearance options such as the menu bar, toolbar, interface language, and multiple layouts, for flexible control over the interface.
Product Composition
Spire.OfficeJS ships three editor modules plus PDF preview, all sharing a single editor entry point and runtime:
| Document Type | Editor Module | Document Engine | Key Capabilities |
|---|---|---|---|
| Word documents | web/editors/spiredocument |
Spire.Doc WebAssembly |
Open, view, edit, save, and "Download as" |
| Excel workbooks | web/editors/spirespreadsheet |
Spire.Xls WebAssembly |
Open, view, edit, save, and "Download as" |
| PowerPoint presentations | web/editors/spirepresentation |
Spire.Presentation WebAssembly |
Open, view, edit, save, and "Download as" |
| PDF documents | — (provided by spiredocument) |
Spire.Pdf WebAssembly |
Read-only preview |
Each editor module offers three interface layouts: desktop, mobile, and embed. For the complete list of supported formats, see Supported File Formats.
Typical Use Cases
| Scenario | Typical Requirement | Role of Spire.OfficeJS |
|---|---|---|
| Document management systems | Online preview, editing, and saving of Office files | Open the editor directly beside the file list, cutting out uploads and downloads |
| OA, approval, and contract systems | Annotations, revisions, permission control, and audit trails | Control edit, comment, and download permissions by role; support review and history |
| Enterprise cloud storage and SaaS platforms | Embed document capabilities into your own product | Fit into existing branding and workflows through configuration and UI customization |
| Online education and training | Assignment grading, courseware preview, and group discussion | Online editing and comments, with read-only mode for presenting materials |
| Archives and knowledge bases | Unified preview across multiple formats | Open Word, Excel, PowerPoint, and PDF documents in a single interface |
How It Differs from Self-Built and Public Cloud Solutions
Integrating document capabilities into a business system involves trade-offs whichever approach you take. First, the download–edit–re-upload workflow is fragmented. Second, building browser-side rendering and editing in-house is costly. Third, relying on server-side conversion adds load. And fourth, public cloud office suites make it difficult to fully align the interface, permissions, and data storage with your business system. Spire.OfficeJS handles document processing inside the browser, covers Word, Excel, PowerPoint, and PDF through a single entry point, and keeps both deployment resources and document data on your side through private deployment—making it well suited to scenarios with requirements around data location, UI branding, and deployment method.
Licensing
Spire.OfficeJS is a commercial component. The license file path is specified through a configuration file in the product package. For details on placing and activating the license, see Spire.OfficeJS License Configuration.
The comments feature in Spire.OfficeJS lets readers leave feedback on a specific passage without changing the body text. Comments are independent of revisions: they can be replied to, resolved, or removed without accepting or rejecting a revision. Using Word as an example, this article describes the comment operations that Spire.OfficeJS provides, and explains how to enable, restrict, or hide them as needed.
Comment Capabilities at a Glance
Comment entry points are available on the Insert and Review tabs, and in the floating button that appears after you select text. The comments pane lists every comment in the document in one place and provides centralized management.
| Capability | UI Location | Description |
|---|---|---|
| Add comment | Insert → Comment; floating button after selecting text | Adds a comment to the selected text |
| Reply to comment | Comment bubble or comments pane | Appends a reply under an existing comment, forming a thread |
| Resolve comment | Comment bubble or comments pane | Marks a handled comment as "Resolved"; the comment stays in the document |
| Reopen comment | Comment bubble | Restores a resolved comment to the unresolved state |
| Edit comment | Comment bubble | Changes the comment text; with commentAuthorOnly enabled, users can only edit their own comments |
| Remove comments | Review → Remove Comments | Removes the current, all, "my" comments |
| Show all comments | Review → Show All Comments | Opens the comments pane to review and manage comments in one place |
| Restrict Editing → Comment | Review → Restrict Editing | Protects the document and allows only comments to be inserted |
The "Remove Comments" drop-down menu provides four options: Remove Current Comments, Remove All Comments (regardless of author), Remove My Comments (only those created by the current user), and Remove My Current Comments. Removing a comment deletes only the comment itself. It does not modify the body text or affect other comments anchored at the same location.
Configure Comment Capabilities
Commenting is switched on or off by canComment in editorAttrs. The pane state on open and the author's editing scope are controlled by the switches under user.customization.public.common.
Expand the Comments Pane on Open
openAllComments controls whether the comments pane expands automatically when a document opens; the default is false. Setting it to true is equivalent to clicking "Show All Comments" right after the document opens, which suits review workflows centered on comments.
user: {
customization: {
public: {
common: {
openAllComments: true // expands the comments pane when the document opens
}
}
}
}

Only Authors Can Edit Their Comments
With commentAuthorOnly enabled, users can edit or delete only the comments and replies they created themselves. Comments from other authors can still be viewed, replied to, or resolved, which suits scenarios that need to preserve the original comment record.
user: {
customization: {
public: {
common: {
commentAuthorOnly: true // only the author can edit their own comments
}
}
}
}

Disable Comments
canComment controls whether commenting is allowed; the default is true. When set to false, the comment button group on the Review tab is hidden. Existing comments in the document remain visible but are read-only. Body editing is unaffected.
editorAttrs: {
canComment: false
}
Note
canComment affects comments only; it does not change body editing or track changes. If the entire document must be read-only, use editorMode: 'view'.

Control Comment-Related Buttons
canComment only switches the commenting capability as a whole. If you want to hide only some buttons (for example, remove the "New Comment" entry while keeping viewing and navigation), configure user.customization.public.common.btnPermissions.docBtnPermissions. Removed buttons and their corresponding shortcuts are disabled as well:
user: {
customization: {
public: {
common: {
btnPermissions: {
docBtnPermissions: [
{ modular: 'Toolbar', subModule: ['btnInsertComments'] }
]
}
}
}
}
}

For more about button permission configuration, see the documentation on customizing the toolbar menu.
FAQ
The New Comment button is unavailable or grayed out
Common causes:
- No text is selected. A comment must be anchored to selected content.
- The cursor is inside a chart, shape, or a content control that does not support comments (such as a check box, picture, or drop-down list).
Comment authors appear as "Guest" or are all identical
Comment authors come from user.name in the current session, and the user identity comes from user.id. Pass a unique, stable id and the correct display name for each user. If no display name is provided, the author appears as "Guest"; if several users share a fixed account, all their comments show the same author.
Spire.OfficeJS supports four document types—Word, Excel, PowerPoint, and PDF. The first three can be opened and edited, while PDF is preview-only. For new documents and long-term storage, use DOCX, XLSX, and PPTX, for interoperability with WPS Office, use WPS, ET, and DPS, for external distribution and archiving, use PDF.
Supported Formats
| Document Type | Editor Module | Supported Operations | Formats |
|---|---|---|---|
| Word documents | spiredocument |
Open and edit | DOCX, DOCM, DOC, DOTX, DOTM, DOT, ODT, FODT, OTT, TXT, WPS, WPT |
| Excel spreadsheets | spirespreadsheet |
Open and edit | XLSX, XLSM, XLS, XLTX, XLTM, XLT, CSV, ET, ETT |
| PowerPoint presentations | spirepresentation |
Open and edit | PPTX, PPTM, PPT, PPSX, PPSM, PPS, POTX, POTM, POT, DPS, DPT |
| PDF documents | spiredocument |
Preview only | PDF, XPS |
- Legacy formats (DOC, XLS, PPT) can be opened and edited, saving them as DOCX, XLSX, and PPTX is recommended.
- RTF, HTML, HTM, and MHT are not in the Word open list and must be converted to DOCX first.
- Missing fonts used by a document cause changes in line breaks and pagination. See Font Management for configuration.
- PDF is preview-only, no editing entry is provided.
Saving and Exporting
Saving usually keeps the original format. File data can be passed to your business system through fileAttrs.callbackUrl or editorAttrs.events.onSave. Downloads are available from “File → Download as…”. The table below lists the formats available in that menu; actual options may vary by version or runtime mode:
| Document Type | Download Formats |
|---|---|
| Word | DOCX, DOC, DOCM, DOTX, DOTM, DOT, PDF, RTF, TXT, HTML, XPS, EPUB, WordXML (Word 2003 XML) |
| Excel | XLSX, XLS, XLSM, XLSB, XLTX, XLTM, XLT, CSV, ODS, ET, ETT, PDF, XPS |
| PowerPoint | PPTX, PPT, PPTM, PPSX, PPS, POTX, POTM, DPS, DPT, PDF, XPS, PS (PostScript) |
| Downloads the original file as-is, format conversion is not available |
When called through downloadAs, the supported range is narrower than the table above. See Conversion and Download for accepted values and examples.
In WebAssembly (serverless) mode, downloads are processed by the engine in the browser, so no separate conversion service is required. Cross-format exports reflow the document, so special fonts and complex layouts may render differently, check each page after exporting.
When a user finishes editing, where the document goes and how it is stored is usually up to the business system. Saving in Spire.OfficeJS leaves that control with you: the editor only generates the current document in the browser, and you can either send it to a backend endpoint or handle it in the page. Whether it overwrites the original document, creates a new version, or saves a copy can follow your own rules. The examples below use Word to show how to save documents with Spire.OfficeJS:
Document saving process
The save takes five steps. "Business page" in the figure refers to the page that hosts the editor; the term is used throughout below.

- The user edits the document.
- The user saves.
- The editor generates the current document.
- With
callbackUrl, the document is sent to that address; withonSave, the business page receives it. - The editor shows "Saved successfully".
Before you save
Saving requires the following configuration:
user.canSaveset totrue(the default).editorAttrs.editorModeset toedit.
Both examples below include these settings. Replace LocalExample with the actual deployment address.
Ways to save a document
Spire.OfficeJS supports two ways to save a document: use callbackUrl when the document should be saved by your backend, and use onSave when the page should handle the save result itself. The steps for each option are described below.
Save a document with callbackUrl
Use this when your backend saves the document. With callbackUrl configured, the editor sends the current document to that address on save, and the endpoint receives and stores it.
- Create an
.htmlpage to open the document. - Configure the save endpoint in
config.fileAttrs.callbackUrl. - Open the page, edit the document, and save. The editor shows "Saved successfully" once the endpoint returns success.
Example configuration:
var config = {
fileAttrs: {
fileInfo:
{ name: 'sample.docx',
ext: 'docx',
primary: '001'
},
sourceUrl: 'http://LocalExample:3000/samples/sample.docx',
callbackUrl: 'http://LocalExample:3001/saveWebAssembly'
},
user:
{ id: 'uid-1',
name: 'Jonn',
canSave: true
},
editorAttrs: {
editorMode: 'edit',
editorType: 'document',
viewLanguage: 'en',
useWebAssemblyDoc: true,
canForcesave: true,
serverless: {
useServerless: true,
baseUrl: 'http://LocalExample:3000'
},
}
};
new SpireCloudEditor.OpenApi('editor', config);
Save a document with onSave
Use this when you have no save endpoint, or you want the page to handle the save result itself. On save, the editor calls onSave and passes the result in event.data:
event.data[0]is the file name, a string such assample.docx.event.data[1]is the current document content, a standard browserFileobject you can download or upload directly.
What you do with these two items is up to the page: download locally, upload to a backend, rename the document, or just show a message. The editor only generates the save result and calls the callback.
Example configuration (do not set callbackUrl in fileAttrs, or set it to empty):
var config = {
fileAttrs: {
fileInfo:
{ name: 'sample.docx',
ext: 'docx',
primary: '001' },
sourceUrl: 'http://LocalExample:3000/samples/sample.docx'
},
user:
{ id: 'uid-1',
name: 'Jonn',
canSave: true
},
editorAttrs: {
editorMode: 'edit',
editorType: 'document',
viewLanguage: 'en',
useWebAssemblyDoc: true,
serverless: {
useServerless: true,
baseUrl: 'http://LocalExample:3000'
},
events: {
onSave: function (event) {
var fileName = event.data[0]; // file name (FileName), e.g. sample.docx
var file = event.data[1]; // file object (File), the current document
var key = event.key; // document key, equal to fileInfo.primary, e.g. 001
// The handling below is entirely up to the page.
// Example: download locally
var url = URL.createObjectURL(file);
var link = document.createElement('a');
link.href = url;
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
setTimeout(function () {
URL.revokeObjectURL(url);
}, 1000);
},
onDocumentStateChange: function (event) {
console.log(event.data ? 'Unsaved' : 'Saved');
}
}
}
};
new SpireCloudEditor.OpenApi('editor', config);
This is what event.data looks like at an onSave :

FAQ
Will saving overwrite the original document automatically
Cause: Spire.OfficeJS does not write the edits back to sourceUrl automatically. Saving only generates the current document and hands it to your business system.
Solution: Handle it in the callbackUrl endpoint or the onSave callback: whether to overwrite the original document, create a new version, or save a copy is up to your business system.
Both callbackUrl and onSave are configured, but onSave does not run
Cause: When both are configured, callbackUrl takes precedence and onSave does not run; with neither, the editor offers no save function.
Solution: Keep only one save outlet. If the page should handle the document itself, remove fileAttrs.callbackUrl or set it to empty.
For many business systems, previewing a document means asking the user to download it and open it locally, or relying on a browser plug-in. Spire.OfficeJS supports opening documents directly in the page, and users do not need to install Office. The document can come from an address the business system can already access or from the user's local selection. This article uses Word as an example to explain how to open documents with Spire.OfficeJS:
Document opening process
The figure and steps below describe the process of opening a document. "Business page" refers to the page in your own business system that hosts the editor.

- The user opens the document through the business page to view or edit it.
- The business page prepares the
config:sourceUrl(orserverless.fileData),fileInfo,user, andeditorAttrs. SpireCloudEditor.OpenApicreates the editor iframe.- In
sourceUrlmode, the browser reads the document from your business storage; infileDatamode, the bytes passed in by the page are used directly. - WebAssembly converts the Office document into the editor's internal format, and the internal JavaScript renders the content on the page.
Ways to open a document
Spire.OfficeJS supports two ways to open a document: use sourceUrl when the document already has an accessible URL, and use fileData when the document comes from the user's local selection and has not been uploaded yet. The steps for each option are described below.
Open a document with sourceUrl
Use this option when the document already has an accessible URL. Create an empty .html file and add the key content following the steps below; replace LocalExample with the actual deployment address.
Add the editor container:
<div id="editor"></div>Include the editor script:
<script src="http://LocalExample:3000/web/editors/spireapi/SpireCloudEditor.js"></script>Prepare the
config. The important parts are the document URL and thewebresource URL:var config = { fileAttrs: { fileInfo: { name: 'sample.docx', ext: 'docx' }, sourceUrl: 'http://LocalExample:3000/samples/sample.docx' }, user: { id: 'uid-1', name: 'Jonn' }, editorAttrs: { editorMode: 'edit', editorType: 'document', viewLanguage: 'en', useWebAssemblyDoc: true, serverless: { useServerless: true, baseUrl: 'http://LocalExample:3000' } } };Initialize the editor:
new SpireCloudEditor.OpenApi('editor', config);
Open a document with fileData
Use this option when the document comes from the user's local selection and has not been uploaded yet. The page structure is the same as in the sourceUrl option (container + editor script); the difference is that you add a file selector and put the document bytes into serverless.fileData.
Add the file selector and the editor container:
<input type="file" id="fileInput" accept=".doc,.docx"> <div id="editor"></div>Read the file, build the
config, and initialize:var opened = false; document.getElementById('fileInput').addEventListener('change', function (e) { var file = e.target.files[0]; if (!file) return; var ext = file.name.split('.').pop().toLowerCase(); var reader = new FileReader(); reader.onload = function (event) { var config = { fileAttrs: { fileInfo: { name: file.name, ext: ext }, sourceUrl: 'offline' // placeholder; it is not requested in fileData mode }, user: { id: 'uid-1', name: 'Jonn' }, editorAttrs: { editorMode: 'edit', editorType: 'document', viewLanguage: 'zh', useWebAssemblyDoc: true, serverless: { useServerless: true, baseUrl: 'http://LocalExample:3000', fileData: event.target.result } } }; var targetId = opened ? 'SpireCloudEditor' : 'editor'; opened = true; new SpireCloudEditor.OpenApi(targetId, config); }; reader.readAsArrayBuffer(file); });
FAQ
sourceUrl is configured but the document fails to load
Cause: The document is read directly by the browser. If sourceUrl cannot be reached, the editor cannot get the document and it will not open.
Solution: Set sourceUrl to an address the browser can open directly, and make sure the document can be accessed in the browser.
Do I need sourceUrl when using the fileData option
Cause: In fileData mode, the editor uses the bytes passed in by the page directly and does not request sourceUrl; however, sourceUrl must not be empty.
Solution: Set sourceUrl to a placeholder value such as offline.
The document content is incomplete and a watermark is shown
Cause: A valid license is not configured, or the license does not match the product that opens the document. Without a license, the editor shows red watermark text in the document and limits the document length, so the content is displayed incompletely.
Solution: Fill in the license field for the corresponding product in editorAttrs; for example, fill in spireDocJsLicense for WordJS. If you use the bundled example system, configure the License Key under server.customization in example/config/default.json.
Contract delivery usually calls for PDF, archiving may call for DOCX, and some pages also need to export HTML or TXT. Spire.OfficeJS converts the document currently in the editor into these formats and hands it to the browser for download. This article uses Word as an example to explain how to convert and download documents with Spire.OfficeJS:
Document conversion and download process
The download takes four steps:

- The user opens a document in the editor.
- The user clicks the convert-and-download button on the page, which calls
docEditor.downloadAs()with the target format. - The editor converts the current document to the target format.
- The browser downloads the document locally.
Using downloadAs
The editor provides the downloadAs() method to convert the current document to a specified format and hand it to the browser for download. You can call it once the editor is initialized; the conversion runs in the browser, and the result is a local copy.
Example configuration (converting to PDF):
var onDownloadAs = function () {
docEditor.downloadAs({ format: 'pdf'});
};
format takes the target format's file extension and is case-insensitive.
Complete code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Spire.OfficeJS Convert File - downloadAs</title>
<style>
html, body { height: 100%; margin: 0; }
body { display: flex; flex-direction: column; }
#editor { flex: 1; min-height: 0; }
#editor iframe { width: 100%; height: 100%; border: 0; }
</style>
</head>
<body>
<button type="button" id="download" onclick="onDownloadAs()" disabled>Download PDF</button>
<div id="editor"></div>
<script src="http://LocalExample:3000/web/editors/spireapi/SpireCloudEditor.js"></script>
<script>
var SERVER = 'http://LocalExample:3000';
var downloadButton = document.getElementById('download');
var config = {
fileAttrs: {
fileInfo: { name: 'sample.docx', ext: 'docx' },
sourceUrl: SERVER + '/samples/sample.docx'
},
user: { id: 'uid-1', name: 'Jonn' },
editorAttrs: {
editorMode: 'edit',
editorType: 'document',
viewLanguage: 'en',
canDownload: true,
useWebAssemblyDoc: true,
serverless: { useServerless: true, baseUrl: SERVER },
events: {
onStart: function () {
downloadButton.disabled = false;
}
}
}
};
var docEditor = new SpireCloudEditor.OpenApi('editor', config);
function onDownloadAs() {
docEditor.downloadAs({ format: 'pdf' });
}
</script>
</body>
</html>
After selecting a document and clicking the download button, the editor converts it and the browser downloads the resulting PDF:

Supported formats
The formats you can convert to depend on the editor that opened the document. Word, Excel, and Presentation load on different pages and support different target formats, so pick the format for the editor that matches the document. The target formats supported by each editor are listed below:
| Editor | Supported formats |
|---|---|
| Spire.WordJS | DOCX, PDF, PDFA, ODT, DOTX, OTT, RTF, HTML, TXT |
| Spire.ExcelJS | XLSX, XLTX, PDF, PDFA, CSV, ODS, OTS |
| Spire.PresentationJS | PPTX, PDF, ODP, POTX, OTP |
FAQ
Does downloading modify the original document or trigger save callbacks
Cause: Downloading does not call callbackUrl or onSave, and it does not write back to sourceUrl; it only generates the document in the browser and downloads it.
Solution: No extra handling is needed. The download is a local copy; neither the original document nor the one in your business system is modified.
format is set, but the downloaded document's format does not change
Cause: If format is unrecognized, the document editor falls back to the document's original format.
Solution: Use the target formats listed for the matching editor in the supported formats table. For example, with sample.docx as the original and format: 'bogus', the downloaded document is still sample.docx.
Can multiple documents be converted at once
Cause: downloadAs() converts only the document currently open in the editor, and one call produces one document in the target format.
Solution: To convert multiple documents, open each document in turn and call downloadAs() for each one, for example by opening the next document after each conversion on the business page.
A valid License is required after Spire.OfficeJS is deployed. Without a License, opened documents show a red trial notice and the content is restricted: Word is limited to 500 paragraphs and 25 tables, Excel to 5 worksheets with up to 200 rows per worksheet, and Presentation and PDF to 10 pages. For details on the restrictions, see the trial version notes on the official download page.
Get a License
To purchase a License or apply for a 30-day temporary License, contact sales@e-iceblue.com. When you contact us, please provide the IP address or domain name of the server you plan to deploy on, and the email address that should receive the license file.
After purchasing or applying for a trial, you will receive a license file in XML format, usually named license.elic.xml. Open the file with a text editor, copy the value of the Key attribute from the root element <License Key="..."> (without the quotes and tags), and fill it into the corresponding field in the table below. The file is digitally signed; do not modify it.
License configuration
The bundled example system and your own frontend project use the same license fields; only the location where you configure them differs. The licensed products and their corresponding fields are:
| Licensed product | License field |
|---|---|
| Spire.OfficeJS | spireOfficeJsLicense |
| Spire.WordJS | spireDocJsLicense |
| Spire.ExcelJS | spireXlsJsLicense |
| Spire.PresentationJS | spirePresentationJsLicense |
| Spire.PDFJS | spirePdfJsLicense |
Whichever product you purchased, fill its License Key into the corresponding field, and leave the fields for products you have not purchased as empty strings "". For example, if you only purchased Spire.WordJS, fill in spireDocJsLicense:
"editorAttrs": {
"spireDocJsLicense": "WordJS License Key",
"spireXlsJsLicense": "",
"spirePresentationJsLicense": "",
"spirePdfJsLicense": "",
"spireOfficeJsLicense": ""
}
If you purchased the Spire.OfficeJS suite license, fill in only spireOfficeJsLicense and leave the other four fields empty. The suite already includes Spire.WordJS, Spire.ExcelJS, Spire.PresentationJS, and Spire.PDFJS.
Where to configure the License depends on how you use Spire.OfficeJS: use the example system bundled with the product package for a quick trial, or integrate the editor into your own frontend project.
Option 1: Use the bundled example system (default port 3000)
When using the example system bundled with the product package, the license fields are located under server.customization in example/config/default.json. Only the license fields are listed below; leave the other settings unchanged:
"customization": {
"spireDocJsLicense": "WordJS License Key",
"spireXlsJsLicense": "",
"spirePresentationJsLicense": "",
"spirePdfJsLicense": "",
"spireOfficeJsLicense": ""
}
After saving, restart the example service for the new configuration to take effect.
Option 2: Integrate into your own frontend project
In your own frontend project, the license fields are written under the editorAttrs object passed to the editor. For example, in Vue:
config.value = {
"editorAttrs": {
"spireDocJsLicense": "WordJS License Key",
"spireXlsJsLicense": "",
"spirePresentationJsLicense": "",
"spirePdfJsLicense": "",
"spireOfficeJsLicense": "",
}
};
Other frameworks work the same way: find editorAttrs in your configuration and fill in the same fields.
For complete integration steps for each framework, see: JavaScript, Vue, React, Angular.
Take effect and verify
After modifying editorAttrs, save and refresh the page for the changes to take effect. When only the frontend configuration is changed, there is no need to restart OfficeServer.
- Vue / React (Vite): hot reload usually applies the changes automatically. If the editor does not reload, refresh the page first; if that still does not work, run
npm run dev. - Angular: if the changes do not take effect, run
ng serve.
After making the changes, open a document of a licensed product and check whether the red trial notice is gone. For a suite license, open one document each in Word, Excel, Presentation, and PDF to verify.