New Froala Editor v5.4.0 is here – Learn More
Froala Documentation
- Installation Guides
- Browser Support
- Languages Support
- Shortcuts
- Activation
- Examples
- Customize the Editor
- Use-cases
- Plugins
- APIs
- Development Frameworks
- Server Integrations
- Server SDKs
- Migration Guides
- Changelog
- Tutorials
- Froala Docs
- /
- SDKs
- /
- Node JS
- /
- Collaboration Overview
Node.JS Collaboration Overview
The Node.js SDK includes an optional backend for the editor's real-time collaboration plugin. It is split into four modules you compose as needed — a WebSocket relay for live peer sync, plus REST + SQLite stores for suggestions & comments, version snapshots, and offline saves. All four can share a single HTTP server and a single SQLite file.
| Module | Purpose |
|---|---|
Collaborative | Pure WebSocket relay. Groups clients by docId and broadcasts messages between peers. |
CollabPersistence | REST + SQLite for track-change suggestions and inline comments. |
VersionControl | REST + SQLite for named or automatic content snapshots. |
AsyncSave | REST + SQLite for saving/loading the latest content in offline (async) mode. |
Prerequisites
- Node.js 18 or later.
- Express — the
CollabPersistence,VersionControl, andAsyncSavemodules register their routes on an Express app. - better-sqlite3 — required by the three persistence modules. It ships as a regular dependency of the SDK and installs automatically
with npm install; no separate step is needed. - Client side — the editor's collaboration plugin manages all CRDT/sync state with Yjs and connects over a standard WebSocket. The server is a transparent relay and never parses message content.
The three persistence modules can share one SQLite file (default collab.db), so a full setup needs just one HTTP server and one database.
Quick start
A single Express-wrapped HTTP server hosts the WebSocket relay and all three REST stores, backed by one shared SQLite file.
const http = require('http');
const express = require('express');
const path = require('path');
const {
Collaborative,
CollabPersistence,
VersionControl,
AsyncSave,
} = require('wysiwyg-editor-node-sdk');
const app = express();
app.use(express.json({ limit: '5mb' }));
// One SQLite file shared by all three persistence modules
const dbPath = path.join(__dirname, 'collab.db');
CollabPersistence.attachRoutes(app, { dbPath });
VersionControl.attachRoutes(app, { dbPath });
AsyncSave.attachRoutes(app, { dbPath });
// Optional: expose live relay stats
app.get('/health', (req, res) => res.json(Collaborative.getStats()));
// Share ONE HTTP server between REST and the WebSocket relay
const server = http.createServer(app);
Collaborative.attachToServer(server);
server.listen(3000, () => {
console.log('Collaborative backend on http://localhost:3000');
});
Use Collaborative.attachToServer(server) whenever the REST routes and the relay must share a port. Use Collaborative.createServer({ port }) only when the relay is the sole service on a port.
URL conventions
WebSocket : ws://<host>/{docId}
REST base : http://<host>/collab/{docId}/{resource} The {docId} used by the WebSocket connection must match the :docId in the REST routes. Document IDs are arbitrary strings; URL-encode any characters outside a-z 0-9 - _.
Connect the editor to the backend
Point the editor at the backend in the plugin config.
Real-time mode
set the WebSocket URL:
new FroalaEditor('#editor', {
collabConfig: {
// Who you are. Other people see this name on your cursor.
// Leave id out and one gets generated for you — but then you're
// a different person every refresh, so set it.
user: {
id: 'user-1',
name: 'Alice',
role: 'editor' // 'editor' | 'suggester' | 'viewer'
},
// WHICH document — think of it as the room number.
// This MUST match the docId in every URL below.
docId: 'my-doc-2024',
// THE LIVE CHANNEL
// Set this and everything syncs instantly between tabs.
// Remove it and the editor drops to offline/async mode.
realTime: {
syncUrl: 'ws://localhost:3000/my-doc-2024'
},
// WHERE COMMENTS ARE STORED. Without this, comments only exist
// while someone is connected — they vanish on the last disconnect.
commentsUrl: 'http://localhost:3000/collab/my-doc-2024/comments',
// Same idea for track-change suggestions.
suggestionsUrl: 'http://localhost:3000/collab/my-doc-2024/suggestions',
// Named snapshots of the document over time.
versionControl: {
url: 'http://localhost:3000/collab/my-doc-2024/versions'
}
},
// Offline mode only: where the editor POSTs the document body.
// Matches AsyncSave's POST /collab/:docId/content
saveURL: 'http://localhost:3000/collab/my-doc-2024/content'
}); Async (offline) mode
omit syncUrl. The editor then persists through the AsyncSave endpoints instead of syncing live. The save plugin posts the content under the field name body (its default saveParam), which matches what POST /collab/:docId/content expects.
Mounting these routes creates the REST endpoints. To actually use them, point the editor at each one via collabConfig.commentsUrl, collabConfig.suggestionsUrl, and collabConfig.versionControl.url.
For the complete endpoint and method reference, see the Collaborative Backend API Reference.
Do you think we can improve this article? Let us know.