Skip to main content
The @veltdev/ace-crdt-react and @veltdev/ace-crdt libraries connect Ace Editor to a Velt CRDT text Store backed by Yjs. Shared content lives in one Y.Text; cursors, selections, and attention highlights use transient awareness state.

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

The current base wrapper peer range targets ace-builds@^1.44.0. Keep one copy of @veltdev/crdt, yjs, and y-protocols in the final application bundle.

Step 2: Setup Velt

Initialize Velt, authenticate a user, and set stable document identity before constructing the collaboration manager. Follow the Velt Setup Docs for the production token endpoint used by authProvider.
All users editing the same file must use the same Velt document ID and Ace editorId.

Step 3: Create the Ace Editor

The host needs stable dimensions. Import each mode and theme before selecting it.
AceCrdtEditor creates a react-ace editor. When using the hook with an editor your application owns, create Ace normally and pass the instance to the hook:

Step 4: Initialize Collaboration

Ace markers require a real Ace Range. The factory below also widens a collapsed cursor by one column so the remote caret stays visible.
Do not pass value or defaultValue to AceCrdtEditor. The shared Y.Text is the content source of truth.

React Context Provider (Optional)

React applications can expose one hook-managed collaboration value through context:
CollaborationProvider is an alias of AceCrdtProvider. Use one provider or one hook for an editor, not both.

Step 5: Add Remote Marker Styles

The manager injects per-user marker colors and labels dynamically. Keep the editor sized and add a shared shape rule:
Cursor markers receive a visible two-pixel left border; selections and highlights receive a border plus a translucent background. Do not override those injected borders with border: none.

Step 6: Monitor Status and Remote Selections

The component render function and hook expose reactive status, synced, remoteSelections, and stats values:
Ace cursor and selection events are published automatically after attachment, and awareness changes trigger remote marker rendering. Custom UI can publish explicitly:
Cursor, selection, and highlight awareness is ephemeral. It is not part of the shared file or a saved version.

Step 7: Initial Content and Force Reset

Initial content is plain text and is applied only when the shared text is new and uninitialized:
Use a reset only for an explicit destructive action:
forceResetInitialContent and forceReset() replace the shared file for every collaborator.

Step 8: Work with Shared Text

insertText() accepts an optional character index. Without one, the manager uses the current Ace cursor when available.

Step 9: Configure Collaborative Undo and Redo

The manager creates one Yjs UndoManager for the shared text.
Use these controls as the user-facing collaborative history. Mixing Ace’s local history commands with Yjs undo can produce a history that does not match the shared document.

Step 10: Save and Restore Versions

Saving flushes Ace before the checkpoint. Restoring updates the Store and reapplies the shared text to Ace.

Step 11: Subscribe to CRDT Events

Step 12: Attach or Replace an Editor (Alternative)

Use the attach-later lifecycle only when the Store must initialize before Ace:
This replaces passing editor to createCollaboration(). Do not use both paths in one initialization flow.

Step 13: Custom Encryption (Optional)

See setEncryptionProvider() and VeltEncryptionProvider.

Step 14: Clean Up

useCollaboration() and AceCrdtEditor unsubscribe and destroy their collaboration manager on unmount. When an app owns the Ace instance, use an explicit manager-first cleanup before destroying Ace:
The hook’s later unmount cleanup is idempotent, so the explicit destroy() safely preserves the required order.

Notes

  • Plain-text model: Ace modes and themes are local presentation; only text is stored in Y.Text.
  • One binding: Passing editor to the factory attaches it automatically.
  • Range factory: Use Ace’s verified Range constructor for remote markers.
  • Automatic selections: The manager attaches Ace selection listeners and publishes cursor/selection state through awareness.
  • Advanced escape hatches: getDoc(), getYText(), getProvider(), getAwareness(), and getUndoManager() are for diagnostics or advanced integrations. getText() and getYText() return the same shared Y.Text; internally, getYText() delegates to getText(). Do not create a second Y.Doc, provider, or shared text.
  • Production authentication: Use a server-backed React authProvider and never expose privileged server tokens in browser code.

Testing and Debugging

  1. Open the same Velt document and Ace editorId as two different authenticated users in separate browser profiles.
  2. Type and delete concurrently at different positions.
  3. Verify visible cursor carets, selected ranges, attention highlights, Yjs undo/redo, versions, reconnect behavior, and persistence.
  4. Repeat with the production mode, theme, worker, and bundler configuration.
Common issues:
  • Mode or theme is missing: import the matching ace-builds/src-noconflict module before creating Ace.
  • Text does not sync: remove React value/defaultValue and confirm both clients use the same IDs.
  • Markers do not appear: provide cursorData, the complete rangeFactory, a sized host, and marker CSS.
  • Duplicate Yjs warning: deduplicate yjs and @veltdev/crdt in the application bundle.
Use the VeltCrdtStoreMap debugging interface to inspect shared text and provider status.

Complete Example

Complete App.tsx
Complete styles.css

How It Works

  1. The app initializes Velt, authenticates the user, sets the document, and creates one Ace editor.
  2. The manager creates a Velt text Store, shared Y.Text, provider, awareness bridge, and Yjs UndoManager.
  3. Ace deltas are converted to character offsets and applied as Yjs insert/delete transactions.
  4. Remote Yjs updates are applied back to Ace without echoing into a second local transaction.
  5. Ace selection events publish awareness state; the range factory converts remote row/column data into visible Ace markers.
  6. Cleanup removes subscriptions and markers before the app destroys Ace and its DOM.

APIs

React: AceCrdtEditor

React: useCollaboration()

The hook returns editorRef, editor and manager state, shared text helpers, selection/highlight helpers, collaborative undo()/redo(), versions, statistics, attachment methods, and destroy(). It includes isSynced, versions, refreshSnapshots(), and refreshVersions() alongside the save and restore helpers. value, remoteSelections, and stats are reactive snapshots.

CollaborationManager Methods

Exported Types and Helpers

  • AceEditorLike, AcePosition, AceSelectionRange, AceDelta, AceRangeFactory
  • AceCursorData, AceAwarenessSelection, RemoteSelection
  • CollaborationConfig, CollaborationPrimitives, CollaborationStats, BindEditorOptions
  • normalizeText(), positionToIndex(), indexToPosition(), rangeToOffsets(), offsetsToRange()