Skip to main content
The @veltdev/spreadjs-crdt-react and @veltdev/spreadjs-crdt libraries enable real-time collaborative workbook editing with MESCIUS SpreadJS. The collaboration engine serializes workbook state into a Velt CRDT Store backed by Yjs.

Prerequisites

  • Node.js (v14 or higher)
  • A Mescius SpreadJS license for production use, as required by Mescius
  • 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

Import the SpreadJS stylesheet in your application entry point:
The current wrapper peer range targets @mescius/spread-sheets@^19.1.3. Use the peer-compatible version, configure a production license before constructing a workbook, and do not commit production license keys to public source control.

Step 2: Create the Workbook

Create a host with stable dimensions, apply the license, and construct the workbook before creating collaboration. The drop-in React component performs these steps internally; React hook and Other Frameworks integrations own the workbook themselves.
Do not create a workbook while its host is display: none, recreate the host while collaboration is active, or remove it before the manager has detached its event handlers and overlays.

Step 3: Setup Velt

React applications initialize Velt at the app root while the workbook component mounts. Other Frameworks integrations initialize Velt after constructing the workbook. In both cases, authenticate the user and set a stable document before creating collaboration. Follow the Velt Setup Docs for the production token endpoint used by authProvider.

Step 4: Initialize Collaborative Workbook

SpreadJSCrdtWorkbook creates one workbook per mount and connects it to the CRDT manager:
The wrapper’s licenseKey prop is optional. When provided, the wrapper applies it before creating the workbook. You can omit the prop if your application configures the license elsewhere, but Mescius licensing requirements still apply to production deployments.
The React hook checks Velt initialization, an authenticated user, a non-empty editor ID, and a workbook unless initializeWithoutWorkbook is enabled. Passing workbook to the factory or hook attaches it automatically; do not call attachWorkbook(workbook) again on the same initialization path.

Share collaboration through React context (optional)

useCollaborationContext() returns null outside the provider instead of throwing.

Step 5: Attach a Workbook Later (Optional)

Initialize the manager before route data or a workbook exists:
Do not call attachWorkbook() after already passing that same workbook to the factory. One manager must not remain attached to multiple live workbooks.

Step 6: Status Monitoring (Optional)

stats reports initialization, connection state, workbook attachment, Store availability, remote selection count, and the last flush reason.

Step 7: Version Management (Optional)

The manager serializes the current workbook before saving. Restoring a version applies the shared workbook JSON to all attached clients.

Step 8: Initial Content and Force Reset (Optional)

initialContent accepts raw SpreadJS workbook JSON or a WorkbookState object:
Use the explicit reset method for reset controls:
For a new Store, the manager applies initialContent and then attaches it to the workbook. Existing persisted state wins during normal initialization. forceResetInitialContent resets during manager creation; forceReset() is the explicit runtime reset. Calling forceReset() without an argument uses configured initialContent, or the current workbook JSON when no seed exists.
forceResetInitialContent and forceReset() replace the complete shared workbook snapshot, including existing user changes.

Step 9: Configure Serialization

The wrapper synchronizes complete workbook snapshots rather than individual cell operations:
  • Local event-driven writes call workbook.toJSON(serializationOptions) after the configured debounce.
  • Remote Store updates call workbook.fromJSON(json, deserializationOptions) while painting and SpreadJS events are suspended.
  • Direct helpers flush immediately, and deterministic snapshot signatures suppress echo loops.
Use actual SpreadJS toJSON() output as seed content and keep both option objects stable for a document family. Test formulas, bindings, named styles, formatting, comments, shapes, and workbook size with the options used in production. This is a workbook-level snapshot strategy, not an operation-level cell CRDT. Overlapping edits to the same cell resolve through the latest accepted snapshot; the wrapper does not provide formula conflict resolution, operational locking, JSON compression, or cell-level merge policies.

Step 10: Cell, Sheet, and Formatting Helpers

The helpers call SpreadJS and then synchronize the resulting serialized workbook. Use normal SpreadJS APIs for operations not covered by these conveniences. Rows and columns are zero-based. Direct SpreadJS cell, sheet, and formatting changes are captured through the official event constants; call flushWorkbookToStore() after complex multi-step operations when immediate persistence is required. Cell/value/range changes, active-sheet changes, sheet changes and renames, and selections are included in the event configuration. Highlights use SpreadJS backColor formatting and persist when the selected serialization options include that formatting.

Step 11: Configure Remote Selections (Optional)

The manager publishes sheet name, ranges, active cell, and user metadata through Yjs awareness. Selection state is transient and is not stored in workbook versions. Call clearRemoteSelectionOverlays() on the manager when replacing the workbook host or implementing custom overlay cleanup. The manager positions its pointer-transparent overlay inside workbook.getHost() by default and renders only selections for the active sheet. Remote selections on other sheets remain available through awareness. The built-in overlay root uses data-velt-spreadjs-remote-selections. Selection rectangles use data-velt-spreadjs-selection, carets use data-velt-spreadjs-caret, and labels expose the collaborator name through data-velt-spreadjs-cursor-name. These attributes are stable hooks for tests and custom styling.

Step 12: CRDT Event Subscription (Optional)

Step 13: Custom Encryption (Optional)

See also: setEncryptionProvider() · VeltEncryptionProvider

Step 14: Error Handling (Optional)

Public methods catch internal failures and return conservative values. Use return values, status, synced, and subscriptions for application control flow.

Step 15: Access Yjs Internals (Advanced)

The manager supplies these Yjs and provider objects. Do not create a second document, provider, or awareness instance, and prefer wrapper methods because direct Yjs mutations bypass workbook application safeguards and snapshot signature tracking.

Step 16: Cleanup

The hook destroys the manager automatically. SpreadJSCrdtWorkbook also destroys the workbook it creates. When using the hook with an application-owned workbook, destroy collaboration before the workbook:

Notes

  • Snapshot model: SpreadJS workbook JSON is synchronized as a structured snapshot, not as independent CRDT cells.
  • Store identity: The manager uses a map Store with content key workbook and source metadata spreadjs.
  • Debounced serialization: Local workbook events are batched before toJSON() and Store writes; the default is 120 ms.
  • Serialization parity: Use compatible serializationOptions and deserializationOptions across clients.
  • Workbook ownership: The component creates and destroys a workbook; the hook accepts an application-owned workbook.
  • Awareness: Cell selections are transient presence state and do not appear in saved versions.
  • Unique editorId: Collaborators must share the same Velt document and editor ID.
  • Persistence: Reopening the same editorId replays the persisted workbook snapshot; unrelated workbooks need different IDs.
  • Required events: Pass GC.Spread.Sheets.Events so workbook, active-sheet, cell, range, value, sheet-name, and selection changes use the official event constants.
  • One attachment path: Passing workbook during creation attaches it automatically. Use attachWorkbook() only when the manager was initialized without that workbook or when replacing a detached workbook.
  • Lifecycle: Destroy the manager before destroying the application-owned workbook or removing its host.
  • One Yjs instance: Deduplicate yjs and y-protocols so the wrapper and application share compatible Yjs constructors.
  • Behavioral boundary: Snapshot synchronization does not provide cell-operation merging, formula conflict resolution, operational locking, JSON compression, or application authorization.
  • Production authentication: Use a server-backed Velt authProvider; never expose privileged server tokens in browser code.

Testing and Debugging

To test collaboration:
  1. Open the same Velt document and SpreadJS editorId as two different users.
  2. Change cell values, formulas, formatting, sheet names, and selections.
  3. Verify remote workbook state, awareness overlays, version restore, and persistence after reload.
  4. Test large workbooks with the intended serialization options and debounce interval.
Common issues:
  • Blank workbook host: Give the host an explicit height and verify the SpreadJS license.
  • Workbook remains loading: Confirm Velt auth is ready and the workbook is attached, or use initializeWithoutWorkbook.
  • Changes are missing: Pass GC.Spread.Sheets.Events and compatible serialization options.
  • Conflicting cell edits overwrite each other: The adapter synchronizes complete workbook snapshots; add a product-level conflict policy for high-risk concurrent operations.
  • Repeated full resets: Disable forceResetInitialContent during normal loads.
  • Overlay drift: Clear and rerender remote selections after sheet or viewport changes.
Use the VeltCrdtStoreMap debugging interface to inspect serialized workbook state and provider status.

Complete Example

Complete App.tsx
Complete styles.css

How It Works

  1. The application loads SpreadJS styles, applies its license, sizes the host, and creates the workbook before the normal collaboration path.
  2. React uses an authenticated VeltProvider; other frameworks initialize one Velt client, authenticate the user, and set the document before creating the manager.
  3. Passing the workbook and GC.Spread.Sheets.Events attaches official workbook and active-sheet listeners during manager initialization.
  4. Local events serialize workbook.toJSON() into a versioned WorkbookState; remote state is applied with workbook.fromJSON() while painting and events are suspended.
  5. Awareness carries transient sheet ranges and active-cell metadata for pointer-transparent remote overlays.
  6. Versions, persistence, and reset operate on the complete serialized workbook snapshot.

APIs

React: SpreadJSCrdtWorkbook

React: useCollaboration()

The hook returns manager, workbook, lifecycle state, versions, remote selections, statistics, attachment helpers, reset/flush helpers, cell/sheet helpers, selection helpers, and destroy().

Other Frameworks: createCollaboration()

Omit workbook only for the documented attach-later lifecycle. Never pass it here and then attach the same instance again.

CollaborationManager Methods

Exported Types and Helpers

  • UseCollaborationConfig / UseSpreadJSCrdtConfig, UseCollaborationReturn / SpreadJSCrdtHookResult
  • SpreadJSCrdtWorkbookProps, SpreadJSCrdtProviderProps, SpreadJSCollaborationManager
  • SpreadWorkbook, SpreadWorksheet, WorkbookJson, WorkbookState, WorkbookStore
  • AwarenessSelection, RemoteSelection, SpreadRange, CollaborationStats
  • cloneJson(), stableStringify(), createWorkbookState(), createSelectionState()
  • isWorkbookState(), hasWorkbookJson(), normalizeRanges()