Skip to main content
What it is: Velt gives you the data and the actions through React hooks, or the equivalent API methods in other frameworks. You build 100% of the UI with your own components. Velt renders nothing.
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.
Use it when: the design needs your own interactive components inside the collaboration UI, a layout wireframe slots can’t express, or you’re rendering comments into a surface Velt can’t draw into (a PDF/canvas/video timeline). (This is the last question in the decision tree.) Don’t use it when: a wireframe could express the layout: headless means you own (and maintain) everything, including parity with Velt’s built-in behavior. It’s the most powerful and the most expensive layer.
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 in Hooks; each maps to an API method on the Velt client for other frameworks, e.g. useAddCommentcommentElement.addComment(), useCommentAnnotationscommentElement.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.
You wire your UI’s events to the mutate hooks and render the read hooks’ data. Velt still handles storage, sync, mentions, permissions: you’re only replacing the view.
Middle ground: keep Velt’s components but strip their visual styling with setUnstyledMode() (v6.0.0-beta.10+), then style everything with your own CSS. See unstyled mode.

Steps

Step 1: Render data from a read source

The read source is reactive: it updates when comments change anywhere (including other users in real time). You never poll.

Step 2: Wire your buttons to actions

Your components are fully interactive (unlike wireframes). Call the actions from your own handlers:

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:
Because you hand-build these, read the type (Comment, CommentAnnotation, User in @veltdev/types) and fill every field the action needs. Missing fields are the most common headless bug.
Minimal 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 on Velt.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 *Request interfaces 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.
If a wireframe can express the layout, prefer it: you keep Velt’s behavior for free and only restyle. Before committing to headless, consider pointing the UI Customization Plugin at your design: it will tell you if a cheaper layer can express it.

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).