Skip to main content
The @veltdev/superdoc-crdt-react and @veltdev/superdoc-crdt libraries enable real-time collaborative editing on SuperDoc Editors. The collaboration editing engine is built on top of Yjs and Velt SDK.

Prerequisites

  • Node.js (v14 or higher)
  • A Velt account with an API key (sign up)
  • React (v18 or higher) when using the React wrapper
  • Optional: TypeScript for type safety

Setup

Step 1: Install Dependencies

Install the required packages:

Step 2: Setup Velt

Initialize Velt, authenticate a user, and set a stable document context before collaboration starts. Follow the Velt Setup Docs for the production token endpoint used by authProvider.
Wrap the app with an authenticated VeltProvider, then set the document after the current user is available:

Step 3: Initialize Collaborative Editor

  • Initialize the collaboration manager and use the returned collaboration config to create the SuperDoc editor.
  • Pass an editorId to uniquely identify each editor instance you have in your app. This is especially important when you have multiple editors in your app.
  • Users with the same Velt document context and editorId edit the same shared document.
Use the useCollaboration hook to manage the entire CRDT lifecycle. It creates a CollaborationManager, initializes the CRDT store, and returns the collaboration config expected by SuperDoc.
Pass the same collaboration object to both the SuperDoc document entry and modules.collaboration. It contains the Velt-backed Y.Doc and sync provider that SuperDoc uses for collaborative editing.

Step 4: Status Monitoring (Optional)

The hook returns reactive status, isSynced, and error state:
For event-style subscriptions, use the manager:

Step 5: Version Management (Optional)

Save and restore named snapshots of the document state. Restored versions are applied to every connected user in real time.

Step 6: Force Reset Initial Content (Optional)

By default, initialContent is applied only when the shared document is brand new. Pass forceResetInitialContent: true to clear the existing shared SuperDoc state during initialization and reapply the initial-content flow.
Use forceResetInitialContent only for deliberate reset-to-template workflows. Do not toggle it during normal re-renders because it clears the current shared document state.

Step 7: Configure Remote Cursors

SuperDoc’s paginated presentation layer renders remote cursors from the shared awareness state. Hide the embedded y-prosemirror cursor decorations to avoid duplicate cursor labels for the same user:
The raw awareness handle is available from primitives.awareness or manager.getAwareness() when you need to build custom presence UI.

Step 8: CRDT Event Subscription (Optional)

Listen for real-time CRDT data events through the optional getCrdtElement() accessor on the Velt client.

Step 9: Custom Encryption (Optional)

Encrypt CRDT data before it is stored in Velt by registering a custom encryption provider. For CRDT methods, input data is of type Uint8Array | number[].
See also: setEncryptionProvider() · VeltEncryptionProvider · EncryptConfig · DecryptConfig

Step 10: Error Handling (Optional)

Pass an onError callback to handle initialization or runtime errors. The reactive error state is also available for rendering.

Step 11: Access Yjs Internals (Advanced)

The hook and manager expose escape hatches for direct Yjs manipulation when needed.
Do not write directly to SuperDoc’s supereditor fragment or clear its parts, meta, or media maps while users are editing. Use resetSuperDocState() only for a deliberate shared reset.

Step 12: Cleanup

The hook destroys the manager automatically when the component unmounts or when the editorId or Velt client changes. Destroy the SuperDoc instance in the same effect that creates it.
Set enabled: false to tear down collaboration early while keeping the React component mounted.

Notes

  • Unique editorId: Use a unique editorId per editor instance.
  • Document context: Users must share both the same Velt document context and editorId to collaborate.
  • SuperDoc config: Pass collaboration to both the collaborative document entry and modules.collaboration.
  • Authenticated user: The hook waits for the Velt client to initialize and for a user to be identified before creating the collaboration manager.
  • React remote cursors: Keep SuperDoc’s presentation cursor overlay enabled and hide the embedded y-prosemirror cursor decorations to avoid duplicate labels.
  • Other Frameworks remote cursors: Disable SuperDoc’s presentation presence overlay and keep the y-prosemirror cursor decorations visible. Use exactly one cursor renderer per integration.
  • Lifecycle: The hook reinitializes collaboration when the editorId or Velt client changes and destroys the previous manager.
  • Other Frameworks lifecycle: Unsubscribe listeners, destroy SuperDoc, and then destroy the manager.
  • Yjs dependency: Keep a single copy of yjs and compatible SuperDoc/wrapper versions in the application bundle.
  • Production authentication: Do not expose privileged server tokens in browser code; use Velt’s recommended server-backed authentication flow.

Testing and Debugging

To test collaboration:
  1. Log in as two unique users in two separate browser profiles and open the same Velt document and SuperDoc editorId in both.
  2. Edit text or formatting in one window and verify the changes and remote cursor appear in the other.
  3. Reload both windows and verify the shared document persists.
Common issues:
  • Editor not loading: Confirm the Velt client is initialized, the user is authenticated, and collaboration is available before creating SuperDoc.
  • Changes not syncing: Confirm both users have the same Velt document context and editorId.
  • Duplicate cursor labels in React: Hide .ProseMirror-yjs-cursor and .ProseMirror-yjs-selection while leaving SuperDoc’s presentation cursor overlay enabled.
  • Duplicate cursor labels in Other Frameworks: Disable layoutEngineOptions.presence in SuperDoc and leave the y-prosemirror cursor decorations visible.
  • Stale editor after changing documents: Destroy the existing SuperDoc instance and let the hook reinitialize with the new editorId.
Enable browser console warnings to see detailed debugging information. You can also use the VeltCrdtStoreMap debugging interface to inspect and monitor your CRDT stores in real time.

Complete Example

A complete collaborative SuperDoc editor with Velt setup, user login, status display, remote-cursor styling, and version management.
Complete App.tsx
Add the remote-cursor styles to your global stylesheet:

How It Works

  1. Velt initialization and authentication happen through VeltProvider in React or initVelt() and identify() in other frameworks. Both paths establish the same stable document context.
  2. The React hook or framework-agnostic factory creates one CollaborationManager for the supplied editorId after Velt is ready.
  3. The manager creates an XML CRDT store backed by a Yjs document and Velt’s real-time sync provider.
  4. getCollaborationConfig() exposes the manager-owned Y.Doc and provider. SuperDoc receives the same instances in its document entry and modules.collaboration configuration.
  5. SuperDoc’s y-prosemirror binding writes text, formatting, and document structure into the shared Yjs document.
  6. Remote changes and cursors arrive through the provider and awareness state. Configure exactly one cursor renderer to avoid duplicate labels.
  7. Version management saves the complete Yjs state as a named snapshot. Restoring a version broadcasts the restored state to all connected users.
  8. React cleanup destroys the manager automatically while the editor effect destroys SuperDoc. Other frameworks unsubscribe callbacks, destroy SuperDoc, and then destroy the manager explicitly.

APIs

Other Frameworks: createCollaboration()

Creates and initializes a CollaborationManager from @veltdev/superdoc-crdt. Pass an initialized, authenticated Velt client, then call getCollaborationConfig() to obtain the { ydoc, provider } pair for SuperDoc.
  • Signature: createCollaboration(config: CollaborationConfig): Promise<CollaborationManager>
  • Cleanup: Call manager.destroy() when you dispose the editor.

React: useCollaboration()

The primary React hook for collaborative SuperDoc editing. It creates a CollaborationManager, initializes the CRDT store, and returns the collaboration config expected by SuperDoc.
  • Signature: useCollaboration(config: UseCollaborationConfig): UseCollaborationReturn
  • Alias: useSuperDocCollaboration
  • Params: UseCollaborationConfig
    • editorId: Unique identifier for the collaborative editor.
    • veltClient: Optional explicit Velt client. Falls back to the VeltProvider context.
    • enabled: Set to false to delay initialization or tear down active collaboration. Default: true.
    • initialContent: Optional initial-content marker applied to a brand-new shared document.
    • forceResetInitialContent: Clear the current shared SuperDoc state during initialization. Default: false.
    • debounceMs: Throttle interval in milliseconds for backend writes. Default: 0.
    • disableAwarenessUser: Disable automatic awareness user setup. Default: false.
    • onError: Callback for non-fatal collaboration errors.
  • Returns: UseCollaborationReturn
    • collaboration: { ydoc, provider } for SuperDoc. null while loading.
    • collaborationConfig: Alias of collaboration.
    • primitives: Lower-level Yjs and Velt collaboration handles. null while loading.
    • isLoading: true until the manager is initialized.
    • isSynced: true after the initial backend sync completes.
    • synced: Alias of isSynced.
    • status: Connection status: 'connecting', 'connected', or 'disconnected'.
    • error: Most recent error, or null.
    • manager: The underlying CollaborationManager, or null before initialization.
    • versions: Reactive list of saved versions.
    • saveVersion: Save a named snapshot and refresh versions.
    • restoreVersion: Restore a saved version by ID.
    • refreshVersions: Refresh and return the saved-version list.

SuperDocCollaborationConfig

The exact collaboration shape expected by SuperDoc’s collaborative document entries and modules.collaboration option.

CollaborationPrimitives

Lower-level handles returned as primitives for advanced integrations.

Version

Saved versions use the following shape:

CollaborationManager Methods

The manager is available from the useCollaboration return value after initialization.

manager.getCollaborationConfig()

Returns the { ydoc, provider } object expected by SuperDoc, or null before initialization.
  • Returns: SuperDocCollaborationConfig | null

manager.getCollaborationPrimitives()

Returns the lower-level collaboration handles, or null before initialization.
  • Returns: CollaborationPrimitives | null

manager.onStatusChange()

Subscribe to connection status changes.
  • Signature: manager.onStatusChange(callback: (status: SyncStatus) => void): Unsubscribe

manager.onSynced()

Subscribe to initial sync state changes.
  • Signature: manager.onSynced(callback: (synced: boolean) => void): Unsubscribe

manager.initialized

Whether initialize() has completed.
  • Returns: boolean

manager.synced

Whether initial sync with the backend has completed.
  • Returns: boolean

manager.status

Current connection status.
  • Returns: SyncStatus ('connecting' | 'connected' | 'disconnected')

manager.saveVersion()

Save a named snapshot and return its version ID.
  • Signature: manager.saveVersion(name: string): Promise<string>

manager.getVersions()

List all saved versions.
  • Returns: Promise<Version[]>

manager.restoreVersion()

Restore a saved version. The restored state is sent to all connected users.
  • Signature: manager.restoreVersion(versionId: string): Promise<boolean>

manager.setStateFromVersion()

Apply a Version object’s state to the current document.
  • Signature: manager.setStateFromVersion(version: Version): Promise<void>

manager.resetSuperDocState()

Clear the shared SuperDoc XML content and metadata maps. This is the low-level reset used by forceResetInitialContent.
  • Returns: boolean
resetSuperDocState() clears the current shared document for every connected user. Use it only for deliberate reset workflows.

manager.getDoc()

Get the underlying Yjs document.
  • Returns: Y.Doc | null

manager.getXmlFragment()

Get the Yjs XML fragment used by SuperDoc.
  • Returns: Y.XmlFragment | null

manager.getProvider()

Get the Velt sync provider.
  • Returns: SyncProvider | null

manager.getAwareness()

Get the Yjs awareness instance used for remote users and cursors.
  • Returns: Awareness | null

manager.getStore()

Get the underlying Velt CRDT store.
  • Returns: Store<string> | null

manager.destroy()

Destroy the store, provider listeners, and awareness state. The React hook calls this automatically during cleanup.
  • Returns: void

Custom Encryption

Encrypt CRDT data before it is stored in Velt by registering a custom encryption provider. For CRDT methods, input data is of type Uint8Array | number[].
See also: setEncryptionProvider() · VeltEncryptionProvider · EncryptConfig · DecryptConfig