Magic-Editor-X — Real-time Collaboration
How it works
- When a user opens a document, a WebSocket connection is opened to
/magic-editor-x/ws. - The client sends its local Y.js state vector; the server returns any differences.
- Every keystroke creates a Y.js update, broadcasted to all connected clients.
- Updates are applied deterministically thanks to CRDT properties — same end result regardless of order.
- Y.js state is periodically flushed to Strapi's database (default every 5 seconds) so page reloads don't lose unsaved work.
Presence awareness
Each connected client broadcasts:
- User ID, username, email
- Random but stable color (for cursor)
- Current selection/cursor position
These are visible to all other editors as colored cursors and highlighted selections.
Tier limits
| Tier | Collaborators / doc |
|---|---|
| Free | 2 |
| Premium | 10 |
| Advanced | Unlimited |
When the limit is reached, additional users enter read-only mode with a banner: "Document is full — wait for someone to disconnect."
Conflict-free merging
Unlike Operational Transformation (OT), CRDT guarantees deterministic convergence without a central coordinator. This means:
- No "last write wins" ambiguity.
- No server-side conflict resolution logic.
- Offline edits merge automatically when reconnected.
Offline support
A user loses connection → they continue typing. The Y.js document accepts edits locally. When the connection returns, all local changes are broadcasted and merged with concurrent changes by others.
Version history (Premium / Advanced)
Every auto-save (every 5 seconds) creates a version snapshot:
- Premium: last 50 versions per document.
- Advanced: unlimited.
Restore a version:
- Open the document.
- Click History in the top-right.
- Pick a version to preview.
- Click Restore.
Versions are immutable — restoring creates a new version at the current HEAD (no data is lost).
Permissions
Collaboration uses the same role-based permissions as regular Strapi Content Manager:
- Users with
readpermission can connect but cannot type. - Users with
updatepermission can edit. deletepermission is needed to hard-delete versions.
Performance tuning
// config/plugins.ts
'magic-editor-x': {
config: {
collaboration: {
enabled: true,
saveInterval: 5000, // ms between auto-saves
maxWsIdle: 300_000, // 5 min idle -> disconnect
maxClientsPerDoc: 10, // override tier default (Premium only)
redisUrl: process.env.REDIS_URL, // for multi-instance deploys
},
},
},Multi-instance deployments
If you run multiple Strapi instances behind a load balancer, configure Redis for cross-instance synchronization:
REDIS_URL=redis://redis-host:6379Without Redis, a user connected to instance A cannot collaborate with a user connected to instance B.
Security
- WebSocket connections are authenticated with the user's JWT.
- Y.js updates are validated against the user's edit permissions.
- CSRF protection — updates must carry a valid CSRF token.
- Rate limits prevent a malicious client from flooding the server with updates.
Debugging
Open browser DevTools → Network → WS tab. You should see:
- Initial sync message (server → client).
- Periodic update messages in both directions.
- Presence updates.
If nothing is flowing:
- Check WebSocket upgrade is working (reverse proxy config).
- Check the client console for auth errors.
- Enable server-side debug:
DEBUG=magic-editor-x:collab npm run develop.
Next: API Reference →