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

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'
    }
  }
});
Need a backend? See our collaboration backend guide

Configuration

modules.collaboration
Object
required
Collaboration module configuration

Required Settings

modules.collaboration.url
string
required
WebSocket server URL
url: 'wss://collab.example.com'
// or for local development
url: 'ws://localhost:3000'

Authentication

modules.collaboration.token
string
Authentication token
Optional if using cookie-based auth on same domain
modules.collaboration.params
Object
Additional connection parameters
params: {
  apiKey: 'project-key',
  roomId: 'custom-room'
}

Provider Type

modules.collaboration.providerType
string
default:"'hocuspocus'"
Provider implementation

User Configuration

Each user must have identifying information:
user
Object
required
Current user information

User Presence & Awareness

Showing Active Users

states
Map
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:
colors
string[]
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

// 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: docxBlob
  },
  {
    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);
});

Performance Tips

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

Security Considerations

Always implement:
  • Server-side authentication
  • Document access validation
  • Rate limiting
  • WSS (encrypted WebSocket) in production
  • Input sanitization

Next Steps