@veltdev/nutrient-crdt-react and @veltdev/nutrient-crdt libraries synchronize Nutrient Instant JSON through a Velt CRDT Store backed by Yjs. The PDF remains a viewer resource; annotations, comments, form values, and transient page selections are the collaborative state.
Prerequisites
- Node.js (v14 or higher)
- A Nutrient Web SDK license for production use, when required by your Nutrient account
- A Velt account with an API key (sign up)
- React (v18 or higher) when using the React wrapper
- A PDF URL,
Blob,ArrayBuffer, or another document input supported by Nutrient - Optional: TypeScript for type safety
Setup
Step 1: Install Dependencies
- React / Next.js
- Other Frameworks
@nutrient-sdk/viewer@^1.17.0. Keep one compatible copy of the viewer and one copy of Yjs in the final application bundle.
Step 2: Create and Load the Viewer
The viewer host must have non-zero dimensions and remain mounted while collaboration is active.position: relative keeps remote overlays aligned with the host.
- React / Next.js
- Other Frameworks
NutrientCrdtEditor can create the host and load the viewer. When using useCollaboration(), create the viewer yourself:load() resolves after teardown. Once a viewer has been accepted into instance, leave normal cleanup to the coordinated manager-first teardown in Step 14 so the viewer is unloaded exactly once after collaboration releases its listeners.useCDN: true for the package-manager setup. If you self-host Nutrient assets, supply the asset/base URL options required by your Nutrient deployment instead. The collaboration wrapper never calls NutrientViewer.load() in the Other Frameworks path.
Step 3: Setup Velt
Authenticate a user and set a stable document context before creating collaboration. Follow the Velt Setup Docs for the server token endpoint used byauthProvider.
- React / Next.js
- Other Frameworks
editorId.
Step 4: Initialize Collaboration
Pass the viewer host asoverlayContainer so the manager can render page-rectangle selections above the viewer.
- React Component
- React Hook
- Other Frameworks
NutrientCrdtEditor creates the host, loads Nutrient, attaches collaboration, and unloads viewers that it created:React Context Provider (Optional)
React applications can expose the same hook value through context:CollaborationProvider is an alias of NutrientCrdtProvider. Use this instead of calling useCollaboration() in multiple descendants for the same viewer.
Step 5: Monitor Status and Sync
- React / Next.js
- Other Frameworks
The hook and component expose reactive
status, synced, error, and stats values. Subscribe through the manager only when an additional callback is needed:Step 6: Initial Instant JSON and Reset Behavior
initialContent is seed data for a new, empty collaborative Store. A client joining an existing document receives persisted Instant JSON instead of overwriting it. The manager strips pdfId before persistence because that identifier is local to a particular PDF load.
Step 7: Flush Viewer Changes
Viewer events schedule debouncedexportInstantJSON() calls. Request an immediate flush after a multi-step custom viewer operation or before navigation when your app must wait for persistence:
- React / Next.js
- Other Frameworks
manager.getStats().lastFlushReason exposes the most recent reason for diagnostics.
Step 8: Work with Document State
applyInstantJson(nextInstantJson, 'import') for a full import. Use getInstantJson() or getDocumentState() to read the current normalized snapshot.
Step 9: Publish and Render Remote Selections
The manager automatically listens for supported Nutrient selection events. For custom viewer tools, publish page-relative rectangles explicitly:Step 10: Save and Restore Versions
Step 11: Subscribe to CRDT Events
- React / Next.js
- Other Frameworks
Step 12: Attach or Replace a Viewer (Alternative)
Initialize without a viewer only when the Store must outlive the viewer:- React / Next.js
- Other Frameworks
instance to the factory. Never use both attachment paths for the same viewer.
Step 13: Custom Encryption (Optional)
- React / Next.js
- Other Frameworks
Step 14: Clean Up
- React / Next.js
- Other Frameworks
useCollaboration() unsubscribes its listeners and destroys its manager on unmount. NutrientCrdtEditor also unloads a viewer that it created, but never unloads an external instance. When your application owns the viewer, release collaboration before unloading the host:viewerAcceptedRef comes from the loading step. It prevents the coordinated cleanup from unloading a viewer whose pending load() promise will instead be handled by the late-load race branch.Notes
- Snapshot model: Nutrient Instant JSON is synchronized as snapshots, not character-level PDF operations.
- Store identity: The manager uses a
mapStore with content keydocumentand source metadatanutrient. - Overlay container: Supply a stable, positioned viewer host so remote page rectangles render in the correct stacking and coordinate context.
- Viewer capability: Full synchronization requires
exportInstantJSON()and an Instant JSON application method supported by the viewer. - One manager: Keep one manager per viewer/document pair.
- Stable IDs: All collaborators must share the same document ID and editor ID.
- Yjs ownership: Use
getDoc(),getMap(),getProvider(), andgetAwareness()only as advanced escape hatches. Do not create a second Y.Doc or provider for the same viewer. - Production authentication: Use a server-backed React
authProviderand never expose privileged server tokens in browser code.
Testing and Debugging
- Open the same PDF, Velt document, and Nutrient
editorIdas two different authenticated users in separate browser profiles. - Add annotations, comments, highlights, and form values from both clients.
- Verify remote selection overlays, version restore, reconnection, and persistence after reload.
- Flush before navigation, then confirm the last local operation persists.
- Destroy and reload the viewer, and separately test the attach-later lifecycle.
- Viewer does not load: verify the document URL, license, CDN/asset configuration, and host height.
- Collaboration remains loading: confirm Velt initialization, an authenticated user, and a viewer instance, or use
initializeWithoutInstance. - Overlay position is wrong: render into the positioned viewer host and transform coordinates for the current page/zoom/scroll state.
- Existing data resets: remove
forceResetInitialContentfrom normal loads.
Complete Example
- React / Next.js
- Other Frameworks
Complete App.tsx
Complete styles.css
How It Works
- The host application loads Nutrient, initializes Velt, authenticates a user, and sets stable document identity.
- The manager creates one Velt Store, Y.Doc, Y.Map, provider, and awareness instance for the viewer.
- Local viewer changes are exported as debounced Instant JSON snapshots; remote snapshots are applied to the attached viewer.
- Awareness carries page rectangles and user metadata for transient overlay rendering.
- Version operations flush and restore the complete normalized Instant JSON state.
- Cleanup releases collaboration listeners and overlays before Nutrient unloads the host.
APIs
React: NutrientCrdtEditor
React: useCollaboration()
CollaborationManager Methods
Exported Types and Helpers
CollaborationConfig,AttachInstanceOptions,CollaborationManager,CollaborationStatsInstantJson,NutrientDocumentState,NutrientViewerInstance,NutrientStoreAwarenessSelection,RemoteSelection,SelectionRectNutrientHighlightInput,NutrientCommentInput,DocumentChangeEventINSTANT_JSON_FORMAT,createCollaboration(),stripPdfId(), and the Instant JSON helper functions

