Hooks are React-only. In other frameworks, the same data and actions live on the
Velt client’s elements (e.g. Velt.getCommentElement()), shown in the Other Frameworks tabs below. The full mapping is in API methods.Headless is the most powerful and most expensive layer: reach for it only when no other layer fits.
The model
Three kinds of hooks (full list grouped inHooks; each maps to an API method on the Velt client for other frameworks, e.g. useAddComment → commentElement.addComment(), useCommentAnnotations → commentElement.getAllCommentAnnotations()):
- Read (data/state):
useCommentAnnotations,useCommentAnnotationById,useUnreadCommentCountOnCurrentDocument,useCommentModeState, … → reactive data you render. - Mutate (actions):
useAddCommentAnnotation,useAddComment,useUpdateComment,useDeleteComment,useDeleteCommentAnnotation,useResolveCommentAnnotation,useUpdateStatus,useToggleReaction,useGetLink, … → call these from your own buttons. - Control:
useVeltClient,useSetDocuments,useIdentify,useVeltInitState, … → init/scope/imperative control.
Steps
Step 1: Render data from a read source
- React / Next.js
- Other Frameworks
Step 2: Wire your buttons to actions
Your components are fully interactive (unlike wireframes). Call the actions from your own handlers:- React / Next.js
- Other Frameworks
Step 3: Build objects to the SDK data model
Mutations expect objects shaped like Velt’s types (from@veltdev/types). You construct them yourself. Example Comment builder:
- React / Next.js
- Other Frameworks
Because you hand-build these, read the type (MinimalComment,CommentAnnotation,Userin@veltdev/types) and fill every field the action needs. Missing fields are the most common headless bug.
Comment for addComment / updateComment (from the Comment type): commentId (number: auto if omitted), type ('text' | 'voice', default 'text'), from (a full User: required), commentText, commentHtml, status ('added' | 'updated'), and the array fields you touch (attachments, taggedUserContacts, reactionAnnotationIds). from is the field most often missed.
What each action expects (the request object)
Actions take a single request object, not loose args. In other frameworks, the same methods live onVelt.getCommentElement() with identical names and request shapes (commentElement.addComment(request), commentElement.updateStatus(request), …). The required fields per common mutation (optional ones omitted; all also accept options?):
Exact, current shapes are the*Requestinterfaces in@veltdev/types(AddCommentRequest,UpdateStatusRequest, …): read the type if a call errors. The table is the field-level companion to the rule above: fill every field the action needs.
What it can and can’t do
Notes & deep-dives
The cost (be honest about this)
Going headless means you re-implement and maintain everything Velt’s UI gave you for free:- Thread layout, replies, collapsing, edit-in-place, resolved state.
- The composer: @mentions UI, attachments, reactions, formatting.
- Filtering/sorting (compute client-side over
useCommentAnnotations()). - Empty/loading states, accessibility, dark mode.
- Upgrade burden: new Velt features won’t appear in your UI automatically.
When headless is the right call
- Comments rendered onto a non-DOM surface (PDF, canvas, a video timeline) where Velt can’t draw.
- A panel built entirely from your own interactive design-system components.
- Behavior that no slot offers (custom inline workflows, bespoke filtering UIs): drive Velt via hooks + the client API.
Checklist
- Using real hook names from
Hooks. - Objects passed to mutations match
@veltdev/types(all required fields). - Reactive data comes from read hooks (no manual polling).
- You’ve confirmed a wireframe genuinely can’t do it (headless is the costly last resort).

