@veltdev/monaco-crdt-react and @veltdev/monaco-crdt libraries enable real-time collaborative editing in Monaco. The collaboration engine is built on Yjs, y-monaco, and the 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
- React / Next.js
- Other Frameworks
yjs, y-protocols, and monaco-editor. For Vite projects, deduplicate them explicitly:
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 byauthProvider.
- React / Next.js
- Other Frameworks
Wrap the app with an authenticated
VeltProvider, then set the document after the current user is available. Keep the document hook unconditional and gate the effect:Step 3: Initialize Collaborative Editor
- Pass an
editorIdto uniquely identify the shared Monaco model. - Do not pass React wrapper
valueordefaultValue, and do not seed a direct Monaco editor with non-empty local text. The CRDT-backed model owns shared content. - Pass
initialContentas plain text for a brand-new shared document.
- React Component
- React Hook
- Other Frameworks
MonacoCrdtEditor is the simplest integration. It creates Monaco, binds its model to Y.Text, and manages collaboration cleanup.@monaco-editor/react props are passed through. The wrapper owns value, defaultValue, and the internal mount handler, but still calls your onMount callback after capturing the editor.Step 4: Status Monitoring (Optional)
Use the hook’s reactive state, component callbacks, or manager subscriptions:- React / Next.js
- Other Frameworks
Local Monaco edits continue while disconnected and synchronize when the provider reconnects.
Step 5: Version Management (Optional)
- React / Next.js
- Other Frameworks
Y.Text for all collaborators. Refresh the version list after each save or restore. For multi-tab interfaces, also refresh on window focus or visibility changes. If peers need immediate metadata updates, broadcast a small awareness event and refresh when they receive it.
Step 6: Force Reset Initial Content (Optional)
initialContent is inserted only when the shared text is new and empty:
- React / Next.js
- Other Frameworks
Step 7: Style Remote Cursors
y-monaco adds remote selection decorations to Monaco. Add base styles:
- React / Next.js
- Other Frameworks
cursorData; when omitted, the adapter falls back to the authenticated Velt user.
Step 8: CRDT Event Subscription (Optional)
- React / Next.js
- Other Frameworks
Step 9: Custom Encryption (Optional)
- React / Next.js
- Other Frameworks
Step 10: Error Handling (Optional)
- React / Next.js
- Other Frameworks
Step 11: Access Collaboration Primitives (Advanced)
- React / Next.js
- Other Frameworks
The hook exposes commonly used primitives:
manager.getUndoManager()?.undo() and redo() for collaboration-aware history. Do not add a separate Monaco history owner for the shared model.
Step 12: Enable, Disable, and Cleanup
- React / Next.js
- Other Frameworks
Delay or disable manager creation while continuing to render Monaco:The component and hook destroy the collaboration manager automatically. The hook also returns
destroy() for early teardown. If you created Monaco yourself, dispose the editor and model separately because the manager does not own them.Step 13: Client-only Rendering
Monaco depends on browser APIs. In Next.js, load the collaborative editor without server-side rendering: Configure Monaco workers in the application bundler as well. For example, a Vite application can install the core editor worker before Monaco is created:- React / Next.js
- Other Frameworks
Notes
- Uncontrolled model: Do not control the wrapper with
valueordefaultValue. A direct Monaco instance may start empty, but the boundY.Textowns shared content. - One dependency instance: Deduplicate
yjs,y-protocols, andmonaco-editorto avoid incompatible constructors and awareness instances. - Unique editorId: Collaborators must use the same Velt document context and
editorId. - Plain-text model: Monaco content is stored in
Y.Text; the Monacolanguageaffects editing behavior, not shared data format. - Store identity: The manager creates a
textStore with content keycontentand source metadatamonaco. - Remote cursors:
y-monacosupplies decorations; your CSS controls labels and colors. - One binding path: Passing
editortocreateCollaboration()auto-binds it. If you omiteditor, callbindEditor(editor)later. Never do both for the same manager. - Multiple editors: Give independent models different
editorIdvalues. Editors sharing one model can be passed throughbindEditor()options. - Other Frameworks lifecycle: Unsubscribe events and cursor-style listeners, destroy the manager, and then dispose the Monaco editor.
- Client-only: Render Monaco only in the browser.
- Production authentication: Use Velt’s recommended server-backed authentication flow.
Testing and Debugging
To test collaboration:- Log in as two unique users in separate browser profiles.
- Open the same Velt document and Monaco
editorId. - Type, select text, undo, and redo in both windows.
- Save and restore a version, then reload and verify persistence.
Yjs was already imported: Deduplicateyjsand remove nested or linked duplicate copies.- Text does not sync: Remove
valueanddefaultValue, and confirm both clients use the same document andeditorId. - Editor never initializes: Confirm Monaco is mounted, Velt is ready, and a user is identified.
- Cursor labels are missing: Add the dynamic client-ID styles or supply your own awareness renderer.
- SSR errors: Load the Monaco component client-side only.
Complete Example
- React / Next.js
- Other Frameworks
Complete App.tsx
Complete monaco-cursors.css
How It Works
- React applications initialize Velt with
VeltProvider; other frameworks initialize one client withinitVelt(). Both authenticate before setting the shared document. - React wrappers wait for Velt, an authenticated user, and a mounted Monaco editor. Other frameworks wait for client readiness and create Monaco before collaboration.
- The manager creates a Velt CRDT store and obtains the shared
Y.Text. - Passing
editorto the factory auto-bindsy-monaco; alternatively, a manager created without an editor can be bound once later withbindEditor(). y-monacomaps awareness selections to editor decorations. Static CSS supplies a visible fallback caret, while dynamic rules apply each collaborator’s color and label.- Local text transactions merge with concurrent remote transactions, and Velt persists the Yjs update stream and named versions.
APIs
React: MonacoCrdtEditor
*Provide either
editorId or documentId. Other Monaco React props are passed through except value and defaultValue.
React: useCollaboration()
Other Frameworks: createCollaboration()
editor and follow the separate alternative in Step 3; never combine the two paths.
CollaborationManager Methods
Exported Types
CollaborationConfig,CollaborationPrimitives,BindEditorOptionsUseCollaborationConfig/UseMonacoCrdtConfigUseCollaborationReturn/MonacoCrdtHookResultMonacoCrdtEditorProps,MonacoCursorData,MonacoBindingInstanceSyncStatus,Unsubscribe,Version

