- Real-time editing over your own transport. Carets, presence, tracked changes, and comments are collaborative by default. Your authentication and infrastructure carry the traffic.
- Word round trip after concurrent edits. Edits merge as CRDT operations,
and the result exports to
.docxwith native tracked changes and comments. - A server in the same room.
@reviseio/editor/serverruns the same converters and document tools in Node. Your backend can create documents before anyone opens them, edit live rooms without a browser, and propose tracked changes for users to review.
One copy of Yjs
Yjs identifies its own types withinstanceof, so your application and the
SDK must resolve to the same Yjs module. Yjs is a peer dependency. Install
it yourself:
Unexpected content type in insert operation. In Vite, keep the
SDK and Yjs on the same side of the dependency optimizer:
vite.config.ts
yjs. Run npm ls yjs to check.
Connect callback
The editor reserves eight top-level keys in the Yjs document (content,
chrome, notes, comments, annotations, metadata, sdt-wrappers,
paragraphs) and needs specific document settings. The recommended form
gives you a document and an awareness instance to connect:
Supplying a Y.Doc
Some transports create the document themselves. Both of these forms work:
A refused document reports the conflicting key:
comments or metadata. Give the
editor its own document, or keep your data in a separate one.
Use the explicit form for an unrecognized transport, a custom sync layer, or a
setup where the document and the connection are separate objects:
Seeding
The first participant writes the content. Everyone else joins it.seed
controls this:
Seeding happens after the first sync. Seeding before receiving the room’s
state would duplicate the document.
Sync state
Setsynced to true once the provider has received the room’s initial state.
Until then, the editor keeps the document closed instead of showing an empty,
editable page:
synced. The editor opens
immediately, and a brand-new room can be seeded twice.
Roster
collaboration.getState() returns awareness as a list of peers, with the same
colors the carets use:
currentUser does not appear.
Presence and cursors
Remote carets render when awareness is available, with a label and color per participant. Peers see what you pass ascurrentUser:
name labels the caret. image is drawn on its flag and can be any URL the
browser can load, including a data: URI.
color is optional. Without it, the editor derives a stable color from the
user id, so each person has the same color for everyone. Set it to use your
own palette or to match an avatar color.
A participant with no currentUser has no caret for others to see. Pass one
for anyone who should be visible.
currentUser also sets the author of tracked changes. Without it,
suggestions export to Word with the reviewer name “Anonymous”.Server editing
With@reviseio/editor/server, your server is a peer in the room, not only a relay. The
backend entry runs the same converters and mutation engine as the editor.
Changes your server makes to the Y.Doc reach every open browser as ordinary
updates, and browser changes reach your server the same way. The backend
reference covers the full API. This section covers
common workflows.
Seeding from your own server
Your server can create the room from a file it already has:seed: "never" and no source file, and join the existing
document. This avoids a race where two people open a new document at the same
time and both import it.
The @reviseio/editor/server converters parse XML and HTML with DOM APIs, so jsdom is
a peer dependency of this entry point. For a worked example of a
@reviseio/editor/server service editing the same live document as the browser, see
reviseio/sdk-demo.
Editing a live room
To change a room on the server, bind a document session to it. The session has the same document tools as the browser: read, search, measure, mutate, and comment. Edits go through the same Yjs-backed mutation engine, so peers receive ordinary updates and remote carets stay in place:mode: "editing" or a per-call
directMode. Nothing an unattended backend job proposes is applied until a
person accepts it.
Review across server and browser
Server and browser share the same tool API.tools.execute() returns the same
ReviseToolResult, mutations report the same suggestionIds, and accept and
reject return the same ReviseSuggestionDecision. The server can propose
changes and a person can resolve them in the browser:
session.listSuggestions(). Each pending record includes
its author, so the job can resolve its own agent’s suggestions and leave human
ones alone. See tracked changes for the
review UI.
Working without a live transport
A server can participate without a socket. For an API endpoint or a queue worker, the client posts its document as one encoded update. The server edits a decoded copy and returns only the delta. The delta merges into the live document like any peer’s edit, even if the user kept typing in the meantime:suggestionIds for the same review flow as above.
What the SDK does not do
- It does not connect. No socket, retries, authentication, or reconnection backoff. Your provider handles all of it.
- It does not persist. Your server decides where the document is stored between sessions.
- It does not destroy your
Y.Doc. A document you pass in outlives the component. Unmounting only clears this client’s caret. - It does not enforce who may edit. Roles limit what each embed can do, but each client sets its own role, and there is no document locking. To enforce a role, check it on the server at the transport. The backend entry lets that server process read and edit the document it guards.