> ## Documentation Index
> Fetch the complete documentation index at: https://velt.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Headless hooks

> Reference Velt headless hooks for reading data, mutating state, and controlling UI.

> [!NOTE]
>
> This page focuses on what you need while customizing UI. The complete canonical reference is [React hooks](https://velt.dev/docs/api-reference/sdk/api/react-hooks.md).

> [!NOTE]
>
> This list is generated from the Velt SDK and is complete for what it covers: a name that isn't listed here doesn't exist; don't guess names. Comment + notification + core hooks are documented in full; the remaining other-feature hooks are listed by name + return type + one-line purpose in "Other features (brief)" below.

The hooks you use to build custom UI ([`Headless`](https://velt.dev/docs/ui-customization/headless)) or to add data-driven extras alongside any approach. All from `@veltdev/react`. Types from `@veltdev/types`.

Grouped by purpose: **read** (data/state you render), **mutate** (actions you call), **events** (callbacks), **control** (init/scope/imperative). This lists the commonly-used comment hooks plus a brief map of other features; the SDK exports many more hooks following the same naming, but everything you need for the customization patterns in these docs is here.

***

## Read: data & state

| Hook | Returns / use |
| - | - |
| `useCommentAnnotations()` | Reactive `CommentAnnotation[]` (or `null` before load) on the current document. The main data source. Use `useCommentAnnotations() ?? []`. |
| `useGetCommentAnnotations(query?)` | Reactive, filtered query → `GetCommentAnnotationsResponse` (`{ annotations, count, … }`), not a bare array. |
| `useCommentAnnotationById({ annotationId })` | One annotation by id. |
| `useCommentAnnotationsCount(query?)` | `GetCommentAnnotationsCountResponse` (read its `count` field), not a bare number. |
| `useCommentModeState()` | Whether comment mode is active. |
| `useCommentSidebarData()` | Data backing the sidebar (filters/sorted list). |
| `useCurrentUser()` | The identified user. |
| `useCurrentUserPermissions()` | Current user's permissions. |
| `useUnreadCommentCountOnCurrentDocument()` | Returns `UnreadCommentsCount`: an **object** `{ count } \| null`, not a number. Read `.count` (e.g. `data?.count ?? 0`). Great for badges. |
| `useUnreadCommentCountByAnnotationId()` / `useUnreadCommentCountByLocationId()` | Unread counts scoped to a thread / location. |
| `useUnreadCommentAnnotationCountByLocationId()` | Unread annotation count by location. |

> **Return shapes matter.** Several read hooks return wrapper objects, not bare values (`…Count*` → `{ count }`; `useGetCommentAnnotations` → a response object). Read the field, don't render the object. When unsure of a return shape, `console.log` the hook's value in your component to see it before rendering.

## Mutate: actions

| Hook | Action |
| - | - |
| `useAddCommentAnnotation()` | Create a new annotation (a new thread). |
| `useAddComment()` | Add a comment/reply to an annotation. |
| `useUpdateComment()` | Edit a comment. |
| `useDeleteComment()` | Delete a single comment. |
| `useDeleteCommentAnnotation()` | Delete a whole thread. |
| `useResolveCommentAnnotation()` | Resolve a thread. |
| `useApproveCommentAnnotation()` | Approval flow. |
| `useUpdateStatus()` | Set a thread's status. |
| `useUpdatePriority()` | Set priority. |
| `useAssignUser()` | Assign a user to a thread. |
| `useUpdateAccess()` | Change a thread's access/visibility. Returns `{ updateAccess }`; `await updateAccess(request: UpdateAccessRequest)` → `Promise<UpdateAccessEvent \| null>`. Writes `CommentAnnotation.iam.accessMode` (public vs private). |
| `useAddReaction()` / `useDeleteReaction()` / `useToggleReaction()` | Reactions. |
| `useAddAttachment()` / `useDeleteAttachment()` / `useGetAttachment()` | Attachments. |
| `useGetLink()` / `useCopyLink()` | Deep link to a comment. |
| `useGetComment()` | Returns `{ getComment }`; `await getComment(request)` → `Comment[]` (imperative fetch, not reactive). |
| `useGetRecording()` / `useDeleteRecording()` | Returns `{ getRecording }` / `{ deleteRecording }`; fetch or delete a recording attached to a comment. |

Most return an object with a function, e.g. `const { addComment } = useAddComment();` then `await addComment({ annotationId, comment })`.

## Events: callbacks

| Hook | Fires on |
| - | - |
| `useCommentEventCallback()` | Generic comment events. |
| `useCommentAddHandler()` / `useCommentUpdateHandler()` | A comment added / updated. |
| `useCommentSelectionChangeHandler()` | Selected thread changes. |
| `useCommentCopyLinkHandler()` | A copy-link action. |
| `useCommentActionCallback(action)` | Typed callback for a specific comment action (`CommentEventTypesMap[action]`). |
| `useCommentSidebarActionButtonClick()` | A custom sidebar action-button click → `CommentSidebarCustomActionEventData \| null`. |
| `useCommentSidebarInit()` | Fires when the comment sidebar initializes. |
| `useCommentDialogSidebarClickHandler()` | A click on the sidebar from a comment dialog. |
| `useNotificationEventCallback(action)` | Typed notification event callback (`NotificationEventTypesMap[action]`). |
| `usePresenceEventCallback(action)` | Typed presence event callback (`PresenceEventTypesMap[action]`). |
| `useRecorderAddHandler()` | Fires when a recording is added → `RecordedData \| null`. |
| `useServerConnectionStateChangeHandler()` | Live-state server connection state changes → `ServerConnectionState`. |
| `useVeltEventCallback(eventName)` | Custom events (e.g. `'veltButtonClick'` from a wireframe button). |

## Control: init, scope, imperative

| Hook | Use | |
| - | - | - |
| `useVeltClient()` | The Velt client (`const { client } = useVeltClient()`); imperative API + `client.getCommentElement()`. (`useClient()` is a newer alias.) | |
| `useIdentify()` | Returns `{ identify }`; call `identify(user)`. | |
| `useSetDocuments()` / `useSetDocument()` | Return a setter: `const { setDocuments } = useSetDocuments();` then `setDocuments(documents, options)`. **Not** `useSetDocuments([...])`. | |
| `useSetDocumentId(documentId)` | Argument-form setter for a single document id (no returned setter; pass the id directly). | |
| `useUnsetDocuments()` / `useUnsetDocumentId()` | Clear the current document(s) scope. | |
| `useSetRootDocument()` | Returns `{ setRootDocument }`; `setRootDocument(document)` sets the root document. | |
| `useSetLocation(location, appendLocation?)` | Argument-form location setter. | |
| `useSetLocations()` | Returns `{ setLocations }`; `setLocations(locations, options)`. | |
| `useSetRootLocation()` | Returns `{ setRootLocation }`; `setRootLocation(location)`. | |
| `useSetPageInfo()` / `useClearPageInfo()` | Return `{ setPageInfo }` / `{ clearPageInfo }`; set or clear page metadata. | |
| `useHeartbeat(config?)` | Reactive `GetHeartbeatResponse` (presence heartbeat). | |
| `useSetContextProvider()` | Provide custom context to comments. | |
| `useVeltInitState()` | Whether Velt finished initializing (gate rendering on this). | |
| `useUiState()` / `useSetLiveStateData()` | Shared UI state between users. | |
| `useSubscribeCommentAnnotation()` / `useUnsubscribeCommentAnnotation()` | Subscribe to a specific annotation. | |
| `useCommentUtils()` | The underlying comment element (\`CommentElement | undefined\`); the imperative element backing most comment hooks. |

***

## Imperative API via the client

Some controls aren't hooks: call them on the comment element:

```tsx theme={null}
const { client } = useVeltClient();
const commentElement = client.getCommentElement();
commentElement.openCommentSidebar();
commentElement.enableCommentMode();
commentElement.setCommentSidebarFilters({ status: ["open"], priority: ["high"] });
commentElement.setCustomStatus([{ id: "new", title: "New", color: "#FF5733" }]);
```

(These element APIs come from the typed comment element returned by `client.getCommentElement()`.)

***

## Other features (brief)

| Feature | Representative hooks |
| - | - |
| Presence | `usePresenceUsers`, `usePresenceData`, `usePresenceUtils`, `usePresenceEventCallback`, `useHeartbeat` |
| Cursors | `useCursorUsers`, `useCursorUtils` |
| Notifications | `useNotificationsData`, `useUnreadNotificationsCount`, `useNotificationSettings`, `useNotificationUtils`, `useNotificationEventCallback` |
| Reactions | `useAddReaction`, `useToggleReaction`, `useDeleteReaction` |
| Recorder | `useRecordings`, `useRecordingDataByRecorderId`, `useRecorderUtils`, `useRecorderEventCallback`, `useRecorderAddHandler` |
| Suggestions | `useSuggestions`, `useSuggestionUtils`, `usePendingSuggestion`, `useStartSuggestion`, `useCommitSuggestion`, `useEnableSuggestionMode` / `useDisableSuggestionMode`, `useSuggestionModeState`, `useSuggestionEventCallback` |
| Activity log | `useAllActivities`, `useActivityUtils` |
| Autocomplete | `useAutocompleteUtils`, `useAutocompleteChipClick`, `useContactList`, `useContactSelected`, `useContactUtils` |
| Tags | `useTagAnnotations`, `useTagUtils` |
| Views/Analytics | `useUniqueViewsByUser`, `useUniqueViewsByDate`, `useViewsUtils` |
| Live state sync | `useLiveState`, `useLiveStateData`, `useSetLiveStateData` |

See [`Other features`](https://velt.dev/docs/ui-customization/customize-by-feature) for how customization applies to these.

### Other-feature hook exports

These are exported from `@veltdev/react` but belong to features outside the scope of these docs. They are listed here so the "a hook not listed doesn't exist" rule holds: name, category, return type, and one-line purpose. Return types come from the SDK's type definitions.

| Hook | Category | Returns | Purpose |
| - | - | - | - |
| `useLiveStateSyncUtils()` | Live state sync | `LiveStateSyncElement \| undefined` | The imperative live-state-sync element backing the live-state hooks. |
| `useUserEditorState()` | Live state sync | `UserEditorAccess \| null \| undefined` | Whether the current user is the editor in single-editor mode. |
| `useEditor()` | Live state sync | `User \| null` | The current editor user in single-editor mode. |
| `useEditorAccessTimer()` | Live state sync | `EditorAccessTimer` (defaults to `{ state: 'idle' }`) | Editor-access countdown/timer state. |
| `useEditorAccessRequestHandler()` | Live state sync | `{ requestStatus: string; requestedBy: User } \| null` | The pending editor-access request, if any. |
| `useLiveStateSyncEventCallback(action)` | Live state sync | `LiveStateEventTypesMap[action]` | Latest live-state-sync event payload (access/editor events). |
| `useRegisterTarget()` | Suggestion targets | `{ registerTarget(config: RegisterTargetConfig<T>): void }` | Register a complex-object getter for a suggestion target. |
| `useUnregisterTarget()` | Suggestion targets | `{ unregisterTarget(targetId: string): void }` | Drop a registered suggestion-target getter. |
| `useLiveSelectionUtils()` | Live selection | `SelectionElement \| undefined` | The imperative live-selection element. |
| `useLiveSelectionDataHandler()` | Live selection | `LiveSelectionData \| null` | Reactive live-selection data. |
| `useCrdtUtils()` | CRDT | `CrdtElement \| undefined` | The imperative CRDT element. |
| `useCrdtEventCallback(action)` | CRDT | `CrdtEventTypesMap[action]` | Latest CRDT event payload (e.g. `updateData`). |
| `useHuddleUtils()` | Huddle | `HuddleElement \| undefined` (typed `any` in source) | The imperative huddle element. |
| `useAIRewriterUtils()` | AI rewriter | `RewriterElement \| undefined` | The imperative rewriter element (subscribe to `textSelected`; call `askAi`/`replaceText`/`addComment`). |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.