Enable multiple users to edit the same document simultaneously with real-time collaboration powered by Yjs and WebSocket connections.
How It Works
SuperDoc uses Yjs , a CRDT (Conflict-free Replicated Data Type) library, to enable real-time collaboration. Changes are synchronized through WebSocket connections to ensure all users see the same document state.
Real-time Sync Changes appear instantly for all connected users
Conflict Resolution CRDTs automatically merge concurrent edits
Offline Support Continue editing offline, sync when reconnected
User Awareness See who’s online with cursors and selections
Quick Start
Check out our fullstack example here to get up and running.
const superdoc = new SuperDoc ({
selector: '#editor' ,
document: 'contract.docx' ,
user: {
name: 'John Smith' ,
email: 'john@company.com'
},
modules: {
collaboration: {
url: 'wss://collab.server.com' ,
token: 'auth-token'
}
}
});
Configuration
Collaboration module configuration
Required Settings
modules.collaboration.url
WebSocket server URL url : 'wss://collab.example.com'
// or for local development
url : 'ws://localhost:3000'
Authentication
modules.collaboration.token
Authentication token Optional if using cookie-based auth on same domain
modules.collaboration.params
Additional connection parameters params : {
apiKey : 'project-key' ,
roomId : 'custom-room'
}
Provider Type
modules.collaboration.providerType
string
default: "'hocuspocus'"
Provider implementation
'hocuspocus' - Default, full-featured
'superdoc' - Legacy provider
User Configuration
Each user must have identifying information:
Current user information Display name shown to other users
Unique identifier for the user
Avatar URL for user presence
User Presence & Awareness
Showing Active Users
Map of user states with their information
superdoc . on ( 'awareness-update' , ({ states }) => {
const activeUsers = Array . from ( states . values ())
. filter ( state => state . user )
. map ( state => ({
name: state . user . name ,
email: state . user . email ,
color: state . user . color ,
cursor: state . cursor ,
selection: state . selection
}));
updateUsersList ( activeUsers );
});
User Colors
Each user automatically gets a unique color from the palette:
Color palette for user awareness colors : [
'#FF6B6B' , '#4ECDC4' , '#45B7D1' ,
'#96CEB4' , '#FFEAA7' , '#DDA0DD'
]
Events
Connection Events
// When collaboration is ready
superdoc . on ( 'collaboration-ready' , ({ editor }) => {
console . log ( 'Connected to collaboration server' );
showOnlineIndicator ();
});
// Monitor connection status
if ( superdoc . provider ) {
superdoc . provider . on ( 'status' , ({ status }) => {
// status: 'connecting' | 'connected' | 'disconnected'
updateConnectionIndicator ( status );
});
superdoc . provider . on ( 'disconnect' , () => {
showReconnectingMessage ();
});
}
Awareness Events
superdoc . on ( 'awareness-update' , ({ states , added , removed }) => {
// New users joined
added . forEach ( clientId => {
const user = states . get ( clientId )?. user ;
if ( user ) showUserJoinedNotification ( user . name );
});
// Users left
removed . forEach ( clientId => {
showUserLeftNotification ( clientId );
});
});
Authentication Strategies
JWT Token
Cookie-Based
API Key
// Frontend
const token = await getAuthToken ();
modules : {
collaboration : {
url : 'wss://server.com' ,
token : token
}
}
Multiple Documents
For multiple collaborative documents in one SuperDoc:
documents : [
{
id: 'doc-1' , // Used as collaboration room
type: 'docx' ,
data: fileObject // File from input
},
{
id: 'doc-2' , // Different room
type: 'docx' ,
url: 'document2.docx'
}
]
// Each document gets its own collaboration room
// based on the document ID
Common Patterns
Connection Monitoring
let reconnectAttempts = 0 ;
const MAX_RECONNECT_ATTEMPTS = 5 ;
superdoc . provider ?. on ( 'disconnect' , () => {
const reconnect = () => {
if ( reconnectAttempts >= MAX_RECONNECT_ATTEMPTS ) {
showError ( 'Unable to reconnect. Please refresh.' );
return ;
}
reconnectAttempts ++ ;
setTimeout (() => {
superdoc . provider ?. connect ();
}, Math . min ( 1000 * Math . pow ( 2 , reconnectAttempts ), 30000 ));
};
reconnect ();
});
superdoc . provider ?. on ( 'connect' , () => {
reconnectAttempts = 0 ;
hideError ();
});
Handling Auth Failures
superdoc . provider ?. on ( 'authenticationFailed' , async ({ reason }) => {
if ( reason === 'token-expired' ) {
// Refresh token
const newToken = await refreshAuthToken ();
superdoc . provider . configuration . token = newToken ;
superdoc . provider . connect ();
} else {
// Redirect to login
window . location . href = '/login' ;
}
});
Offline Support
let offlineQueue = [];
superdoc . provider ?. on ( 'disconnect' , () => {
// Queue changes while offline
superdoc . on ( 'editor-update' , queueOfflineChange );
});
superdoc . provider ?. on ( 'connect' , () => {
// Sync queued changes
offlineQueue . forEach ( change => {
applyChange ( change );
});
offlineQueue = [];
superdoc . off ( 'editor-update' , queueOfflineChange );
});
For optimal performance:
Limit active users per document (recommended: < 50)
Use debounced saves (2-5 seconds)
Enable compression for WebSocket
Monitor bandwidth usage
Troubleshooting
Connection Issues
Check:
Correct protocol (ws:// for local, wss:// for production)
CORS settings on server
Firewall/proxy configuration
Authentication token validity
Verify:
Both users in same document/room
Server receiving updates (check logs)
No network throttling
Document IDs match
Consider:
Server location (use CDN/edge)
Message size (batch updates)
Connection quality
Number of concurrent users
Security Considerations
Always implement:
Server-side authentication
Document access validation
Rate limiting
WSS (encrypted WebSocket) in production
Input sanitization
Next Steps