@veltdev/quill-crdt-react and @veltdev/quill-crdt libraries connect Quill 2 to a Velt CRDT text Store backed by Yjs. Rich text is represented as Quill Delta operations on one Y.Text, the base manager owns the y-quill binding and Yjs undo manager, and quill-cursors renders awareness cursors.
Prerequisites
- Node.js (v14 or higher)
- Quill 2
- 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
quill@2.0.3, y-quill@1.0.0, and quill-cursors@4.3.0. Resolve one compatible copy of yjs across @veltdev/crdt, @veltdev/quill-crdt, and y-quill.
Step 2: Load Styles and Register Cursors
Import the stylesheet for the selected Quill theme:- React / Next.js
- Other Frameworks
QuillCrdtEditor dynamically loads Quill and registers quill-cursors in the browser. Register it yourself before creating a custom Quill instance:new Quill(). Without it, document sync can still work, but remote cursor labels and selections will not render.
Step 3: Configure the Quill Editor
- React / Next.js
- Other Frameworks
The drop-in component creates Quill internally. For the hook path, define the options now and construct Quill in a client-only effect only after the Velt document scope in Step 4 is ready:
userOnly: true. Expose the manager’s Yjs-aware undo() and redo() methods as the user-facing collaborative history controls.
Step 4: Setup Velt
Initialize Velt, authenticate a user, and set stable document identity before creating collaboration. Follow the Velt Setup Docs for the production token endpoint used byauthProvider.
- React / Next.js
- Other Frameworks
editorId.
Step 5: Initialize Collaboration
- React Component
- React Hook
- Other Frameworks
QuillCrdtEditor registers cursors, creates Quill, and manages the collaboration lifecycle:React Context Provider (Optional)
React applications can share one hook-managed collaboration value with descendant controls:CollaborationProvider is an alias of QuillCrdtProvider. Use one provider or one hook for an editor, not both.
Step 6: Add Editor and Cursor Styles
quill-cursors supplies cursor geometry and labels. Do not hide .ql-cursor or .ql-cursor-selection in application CSS.
Step 7: Initial Content and Reset Behavior
initialContent accepts plain text or a Quill Delta:
Step 8: Monitor Status and Remote Selections
- React / Next.js
- Other Frameworks
The component render function and hook expose reactive
status, synced, remoteSelections, and stats:y-quill binding publishes normal editor selection changes and quill-cursors renders them. If custom selection UI changes the range outside that normal flow, publish explicitly:
Step 9: Work with Text and Delta Content
highlightRange() for a persistent Delta background attribute. This differs from a remote awareness selection:
Step 10: Configure Collaborative Undo and Redo
Use the manager’s Yjs-aware history methods:undoCaptureTimeout controls how quickly adjacent local operations are grouped. The manager-scoped Y.UndoManager is the collaborative history source; do not wire your user-facing controls to a separate Quill history stack.
Step 11: Save and Restore Versions
Step 12: Subscribe to CRDT Events
- React / Next.js
- Other Frameworks
Step 13: Attach or Replace an Editor (Alternative)
Initialize the Store without Quill only when it must outlive the editor:- React / Next.js
- Other Frameworks
editor to createCollaboration(). Detaching destroys the active y-quill binding but leaves the Store running.
Step 14: Custom Encryption (Optional)
- React / Next.js
- Other Frameworks
Step 15: Clean Up
- React / Next.js
- Other Frameworks
useCollaboration() unsubscribes and destroys its manager on unmount. QuillCrdtEditor also clears the host that it created. When an app owns the custom Quill host, explicitly release collaboration before removing its DOM:destroy() safely preserves the required order.Step 16: Client-only Rendering
Quill requires browser APIs. The drop-in React component dynamically imports Quill, but the route containing it must still be a client component in an SSR framework:Notes
- Rich-text model: Delta text, attributes, and embeds are stored on one shared
Y.Text. - Binding ownership:
@veltdev/quill-crdtowns the onlyy-quillbinding, provider, awareness instance, and Yjs undo manager. - Cursor module: Register
quill-cursorsbefore a customnew Quill()call. - Persistent versus transient state: Delta background highlights persist; awareness selections do not.
- Single Yjs copy: Run
npm ls yjs(or the package-manager equivalent) and deduplicate if more than one version appears. - Advanced escape hatches: Use
getDoc(),getYText(),getProvider(), andgetAwareness()only for diagnostics or advanced integrations. Do not create a second Y.Doc, Y.Text, provider, or directQuillBinding. - Production authentication: Use a server-backed React
authProviderand never expose privileged server tokens in browser code.
Testing and Debugging
- Open the same Velt document and Quill
editorIdas two different authenticated users in separate browser profiles. - Edit text, formatting, lists, links, embeds, and highlights concurrently.
- Verify remote cursor labels, selected ranges, manager undo/redo, versions, reconnection, and persistence.
- Test with the production
modulesandformatsallowlist. - Run
npm ls yjsand confirm the application resolves one Yjs copy.
- Cursors do not render: register
quill-cursorsbefore editor creation, enable thecursorsmodule, and keep cursor CSS visible. - Formatting disappears: include that format in Quill’s
formatsallowlist. - Editor remains loading: confirm Velt initialization, authentication, and the captured Quill instance, or use
initializeWithoutEditor. - Duplicate Yjs warning: deduplicate
yjs,@veltdev/crdt, andy-quill.
Complete Example
- React / Next.js
- Other Frameworks
Complete App.tsx
Complete styles.css
How It Works
- The app loads the theme and registers
quill-cursors, initializes Velt, authenticates the user, sets stable document identity, and then creates Quill before creating collaboration. - The manager creates one Velt text Store, shared
Y.Text, provider, awareness instance, YjsUndoManager, andy-quillbinding. - Quill Delta changes and remote Yjs transactions flow through that single binding.
- The binding publishes selection positions through awareness, and
quill-cursorsrenders remote cursors and labels. - Manager undo/redo tracks collaborative Yjs transactions rather than a standalone Quill history stack.
- Cleanup unsubscribes callbacks, destroys the binding and manager, then removes Quill’s DOM.
APIs
React: QuillCrdtEditor
React: useCollaboration()
CollaborationManager Methods
Exported Types
QuillEditorLike,QuillDelta,QuillRange,QuillCursorData,RemoteSelectionQuillModules,QuillFormats,QuillCursorsModule,BindEditorOptionsCollaborationConfig,CollaborationPrimitives,CollaborationStats,SyncStatus,Unsubscribe,Version

