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

# Behaviors & composition

> Reference default behaviors, prop interactions, and positioning ownership across Velt components.

[`Props`](/docs/ui-customization/reference/props) tells you a prop's name, type, and default. This page tells you **what it actually does**: how Velt behaves with no props at all, which props fight each other, and what you take over when you leave wireframes.

<Note>
  Generated from the Velt SDK. A name that isn't listed here doesn't exist: don't guess.
</Note>

## Per-component behavior: every prop (exhaustive)

Every prop's runtime effect, per component. These pages are source-derived, so they win on any conflict with other docs.

| Component group                                                                                                                   | File                                                                                       |
| --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| `VeltComments` (core), all \~129 props                                                                                            | [`Comments core`](/docs/ui-customization/reference/behaviors/comments-core)                     |
| `VeltCommentsSidebar` (V1), `VeltCommentsSidebarV2`, `VeltSidebarButton`                                                          | [`Sidebar`](/docs/ui-customization/reference/behaviors/sidebar)                                 |
| `VeltCommentDialog`, `VeltMultiThreadCommentDialog`, `VeltCommentText`, `VeltInlineCommentsSection`, `VeltInlineReactionsSection` | [`Dialog and inline`](/docs/ui-customization/reference/behaviors/dialog)                        |
| `VeltCommentPin`, `VeltCommentBubble`, `VeltCommentTool`                                                                          | [`Pins, bubbles, and tools`](/docs/ui-customization/reference/behaviors/pins-tools)             |
| `VeltNotificationsPanel`, `VeltNotificationsTool`                                                                                 | [`Notifications`](/docs/ui-customization/reference/behaviors/notifications)                     |
| `VeltPresence`, `VeltCursor`, `VeltReactionTool`                                                                                  | [`Presence, cursors, reactions`](/docs/ui-customization/reference/behaviors/presence-reactions) |
| `VeltRecorderControlPanel`, `VeltRecorderPlayer`, `VeltVideoPlayer`, `VeltVideoEditor`, `VeltHuddle`, `VeltUserInviteTool`        | [`Recorder, video, huddle`](/docs/ui-customization/reference/behaviors/recorder-huddle)         |

Events are in [`Events`](/docs/ui-customization/reference/events); data fields in [`Data models`](/docs/ui-customization/reference/data-models).

## Default behaviors (no prop needed)

What Velt does out of the box. These are the baseline a prop modifies.

| Behavior                                                     | Default                                                                                                                                                     | How to change it                                                                                                            |
| ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| **Composer hidden until the dialog is selected**             | On an unselected pin dialog that already has comments, the composer doesn't render. A thread with no comments always shows it.                              | No "always show composer" prop exists on the pin dialog. Use `persistentCommentMode` or a standalone `VeltCommentComposer`. |
| **Composer opens collapsed**                                 | `composerMode: 'default'` renders it single-line, expanding to full actions on focus.                                                                       | `composerMode="expanded"` starts it open.                                                                                   |
| **Dialog shows a preview, not the full thread**              | An unselected pin dialog shows the first comment only. `showAllComments` defaults `true`, so selecting it shows all.                                        | `collapsedComments`, `collapsedRepliesPreview`, `fullExpanded`. See the matrix below.                                       |
| **Dialog body scrolls**                                      | The thread list is a scroll container and auto-scrolls to the bottom on a new comment.                                                                      | No prop disables it. Restructure with a [thread-card wireframe](/docs/ui-customization/layout).                                  |
| **Selecting a pin expands the dialog**                       | With `expandOnSelection: true` (default), selecting reveals all comments and the composer.                                                                  | `expandOnSelection={false}` selects without expanding.                                                                      |
| **Sidebar filters restore from the session**                 | The sidebar re-applies the last-used filters on open, **overriding `defaultMinimalFilter`**.                                                                | `defaultMinimalFilter` only applies when the session has no saved filter.                                                   |
| **Mentions, reactions, attachments, status, resolve are on** | `userMentions`, `reactions`, `attachments`, `status`, `resolveButton`, `seenByUsers` default on. `priority`, `visibilityOptions`, `screenshot` default off. | [`Feature flags`](/docs/ui-customization/reference/feature-flags).                                                               |
| **Enter inserts a newline**                                  | `enterKeyToSubmit` defaults off, so Cmd/Ctrl+Enter submits.                                                                                                 | `enterKeyToSubmit={true}` makes Enter submit.                                                                               |
| **Dialog and pin are shadow-DOM wrapped**                    | `dialogShadowDom` / `pinShadowDom` default `true`.                                                                                                          | Set the per-surface flag or master `shadowDom` to `false`.                                                                  |

## Prop-interaction matrix

### `collapsedComments` × `collapsedRepliesPreview`

The most misunderstood pair. Which comment cards render depends on three things: whether the dialog is **selected**, `showAllComments` (default `true`), and `collapsedRepliesPreview`.

| State                                                             | What renders                                                |
| ----------------------------------------------------------------- | ----------------------------------------------------------- |
| Selected, `showAllComments` true (default)                        | Every comment.                                              |
| Selected, `showAllComments` false (what `collapsedComments` sets) | First + last only, with a "N more replies" divider between. |
| Unselected, `collapsedRepliesPreview` true                        | The same first + last teaser, before selection.             |
| Unselected, default                                               | First comment only.                                         |

```
┌─────────────────────────────┐
│  first comment   (index 0)  │
│  ── N more replies ──       │
│  last comment  (index n-1)  │
└─────────────────────────────┘
```

| Prop                      | Default | What it does                                          |
| ------------------------- | ------- | ----------------------------------------------------- |
| `collapsedComments`       | `false` | Collapses even a **selected** dialog to first + last. |
| `collapsedRepliesPreview` | `false` | Shows that teaser in the **unselected** state.        |

<Warning>
  **First + second + last is not achievable with props.** The collapsed view is strictly `index === 0 || index === length - 1`. There's no prop for "first three" or "first + newest two". For any other pattern, use a [thread-card wireframe](/docs/ui-customization/layout) or [primitives](/docs/ui-customization/primitives). `maxReplyAvatars` (default `3`) caps **avatars**, not comment cards.
</Warning>

### `sortBy` × `sortOrder`

`sortBy` picks the field (`'createdAt' | 'lastUpdated'`), `sortOrder` the direction (`'asc' | 'desc'`). They apply together to the comment list in the dialog and inline section. With neither set, comments render in creation order. They only reorder; they don't hide cards.

On the inline section, the older `sortData` is deprecated: pass `sortBy` + `sortOrder` instead.

### `visibilityOptions` (requires `isPrivateCommentsEnabled`)

`visibilityOptions` (default `false`) shows the per-comment visibility picker. It's **gated**: the banner renders only when the SDK's private-comments capability is also on. Turning it on without that shows nothing. `privateCommentMode` makes comments private by default.

### `shadowDom` vs `dialogShadowDom`

`shadowDom` is the master wrap for `VeltComments`. The per-surface flags (`dialogShadowDom`, `pinShadowDom`, `textCommentToolShadowDom`, …) default `true` and isolate each surface independently.

To style a surface with your own CSS, turn off the shadow DOM that wraps **it**: `dialogShadowDom={false}` reaches dialog internals, `pinShadowDom={false}` reaches the pin. Master `shadowDom={false}` drops isolation everywhere. A per-surface `true` keeps that surface isolated even when others are off. See [`CSS classes`](/docs/ui-customization/reference/css-classes) for what becomes selectable.

### `defaultMinimalFilter` (overridden by saved state)

Sets the sidebar's initial quick-filter (`'all' | 'unread' | 'resolved' | 'open' | 'assignedToMe' | …`). **Saved session state wins**, so this is a first-run default, not a forced value. To force a filter on every open, set it programmatically with `setCommentSidebarFilters` ([`Layout config`](/docs/ui-customization/reference/props#part-3-layout-config-and-custom-data)).

### `embedMode` / `pageMode` (sidebar)

* **`embedMode`** renders the sidebar inline in your layout instead of docked. **The type differs by version:** `boolean` on V1, `string` on V2. It makes `position` and `floatingMode` moot, since you own placement.
* **`pageMode`** switches to page-level comments: no pins on the DOM, and the composer lives at the sidebar level (`pageModePlaceholder`, `pageModeComposerVariant`). Mutually exclusive with pin-based commenting.

### `fullExpanded` vs default

`fullExpanded` (default `false`) forces threads fully expanded, with message text untruncated. It's the opposite of `collapsedComments`: don't set both. `messageTruncation` (+ `messageTruncationLines`, default `4`) is a separate axis that truncates **long single messages**, independent of how many cards show.

### `commentPlaceholder` vs `replyPlaceholder` priority

The composer's placeholder resolves in this order:

1. **Generic `placeholder`** overrides everything.
2. Otherwise the thread's comment count decides: **no comments → `commentPlaceholder`**, **has comments → `replyPlaceholder`**.
3. Otherwise a built-in default that varies with `userMentions`.

So the two never both apply at once. Edit mode has its own chain: `editPlaceholder` → `editCommentPlaceholder` / `editReplyPlaceholder`, again with generic `placeholder` on top.

## Dialog state machine

Selection state and composer state are tracked **per dialog instance**, so the sidebar list, a focused thread, and a floating pin dialog for the same annotation don't leak state into each other.

**Dialog selection:**

```
unselected ──(click pin / select)──▶ selected ──(expandOnSelection: true, default)──▶ expanded
   │                                     │
   │ shows: first comment only           │ shows: composer + all comments
   │ (or first+last if                   │ (or first+last if collapsedComments)
   │  collapsedRepliesPreview)           │
   ◀──(deselect / close / Esc)───────────┘
```

* **unselected → selected:** clicking the pin, or via `dialogOnHover` / `dialogOnTargetElementClick`.
* **selected → expanded:** automatic when `expandOnSelection` is true. Set it `false` and selection won't expand: the dialog stays a preview and the composer stays hidden.
* **any → unselected:** deselect, close, or `forceCloseAllOnEsc`.

**Composer state** (independent, per instance):

```
collapsed ──(focus / click composer)──▶ open ──(start recording)──▶ recording ──(stop)──▶ open
   ▲                                       │
   └──────────(submit / clear)─────────────┘
```

* **collapsed → open:** focusing the composer. With `composerMode: 'expanded'` it starts open and never collapses.
* **open → recording:** a recorder config is active. Recording takes precedence over open.
* **open → collapsed:** on submit or clear (text, attachments, recordings, and tagged users all reset).

## Variant → context scoping

The same dialog renders in several contexts, and **each has its own variant prop**. This is how you make the sidebar-opened dialog look different from the floating pin dialog.

| Context                               | Variant prop                                               |
| ------------------------------------- | ---------------------------------------------------------- |
| **Floating pin dialog** (the default) | `variant` on `VeltComments`                                |
| **Sidebar-opened dialog**             | `dialogVariant` on the sidebar or sidebar button           |
| **Focused thread**                    | `focusedThreadDialogVariant` on the sidebar                |
| **Page-mode composer**                | `pageModeComposerVariant` on the sidebar or sidebar button |
| **Inline section dialog**             | `dialogVariant` / `composerVariant` on the inline section  |

Register a named [wireframe variant](/docs/ui-customization/layout) and point only the relevant prop at it. Setting the base `variant` on `VeltComments` changes the **floating pin dialog only**; the other contexts keep their defaults.

<Tip>
  "The dialog changed in the sidebar but not on pins" (or the reverse) is almost always the wrong variant prop for the context.
</Tip>

## Positioning ownership (anchored surfaces)

`VeltComments` owns pin placement and dialog anchoring. The dialog component takes no position or coordinate prop and doesn't place itself: it asks the host to re-anchor when its size changes (composer expands, recording starts), and the host does the placement. `targetElementId` names an anchor element, not a coordinate.

**What each approach forfeits:**

| Approach       | Positioning                                                                                                                                                                  |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Wireframes** | You keep Velt's anchoring for free. Pins drop where clicked, dialogs anchor and re-position on resize. This is why wireframes are the cheapest option for anchored surfaces. |
| **Primitives** | You own the container. Placing your dialog *at the pin* is your job.                                                                                                         |
| **Headless**   | You own everything: pin position, dialog position, re-anchor on resize.                                                                                                      |

<Warning>
  The public API doesn't expose a pin's resolved screen coordinates, so a custom primitive or headless dialog **cannot** be anchored to a Velt pin's position.
</Warning>

**Which surfaces Velt positions:**

| Velt-positioned                                      | You position                                          |
| ---------------------------------------------------- | ----------------------------------------------------- |
| Pin comment **dialog** (anchors to its pin)          | **Sidebar** (docks, or `embedMode` inline)            |
| **Pins** (drop where clicked)                        | **Inline comments section** (sits where you mount it) |
| **Bubbles** (attach to a target element)             | **Notifications panel** (where you mount it)          |
| **Cursors** (track live pointers)                    |                                                       |
| **Minimap** (corner placement via `minimapPosition`) |                                                       |
