Add Velt text comments to a Nutrient Web SDK PDF viewer with the Velt Nutrient comments extension.
The Nutrient integration renders each Velt comment as a view-only overlay positioned over the commented PDF text with Nutrient CustomOverlayItems. It does not modify your PDF. Comment anchors are stored as a durable { text, pageNumber, occurrence } object and re-resolved against the page text whenever comments render.
npm i @veltdev/nutrient-velt-comments @nutrient-sdk/viewer@1.15.1
@nutrient-sdk/viewer is a peer dependency used for types. Use the same Nutrient version that you load from the CDN script. Load the Nutrient Web SDK at runtime so your app does not bundle the browser-only PDF viewer and WebAssembly assets.
React / Next.js
import Script from 'next/script';const NUTRIENT_CDN_SCRIPT = 'https://cdn.cloud.nutrient.io/pspdfkit-web@1.15.1/nutrient-viewer.js';export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body> <Script src={NUTRIENT_CDN_SCRIPT} strategy="afterInteractive" /> {children} </body> </html> );}
If you self-host Nutrient assets, copy node_modules/@nutrient-sdk/viewer/dist/ into your static folder and load the SDK script from that path. The Velt Nutrient package expects the runtime SDK namespace on window.NutrientViewer.
Step 3: Configure the Nutrient viewer with the Velt Comments extension
Load the Nutrient viewer, then attach NutrientVeltComments to the viewer instance after NutrientViewer.load(...) resolves. The same instance is used by addComment, captureSelection, and renderComments.
Step 4: Add a comment button to your Nutrient viewer
Add a button that users can click after selecting text in the PDF. Call captureSelection(instance) before addComment({ instance }) so the Velt Nutrient package records the current Nutrient selection before focus moves to your host UI.
React / Next.js
import { addComment, captureSelection } from '@veltdev/nutrient-velt-comments';const handleAddComment = async () => { if (!instance) return; await captureSelection(instance); const result = await addComment({ instance }); if (!result) { console.warn('Select text in the PDF before adding a comment.'); }};<button onMouseDown={(event) => event.preventDefault()} onClick={handleAddComment}> Add Comment</button>
commentAnnotations: Array of Comment Annotation objects.
React / Next.js
import { useEffect } from 'react';import { useCommentAnnotations } from '@veltdev/react';import { renderComments } from '@veltdev/nutrient-velt-comments';const commentAnnotations = useCommentAnnotations();useEffect(() => { if (!instance) return; renderComments({ instance, commentAnnotations: commentAnnotations ?? [], });}, [instance, commentAnnotations]);
Highlights are view-only. The library re-derives each highlight from its TextEditorConfig anchor and Nutrient repositions the overlays across scroll, zoom, and page changes. Re-run renderComments when Velt’s annotation list changes or when you unload and reload the document.
NutrientVeltComments.configure(...).attach(instance) returns an AttachedExtension. Call detach() in your effect cleanup to remove listeners, clear per-instance state, and remove overlay elements before unloading the viewer.