Skip to main content
Source of truth: exhaustive for what it covers. Generated from Velt customization metadata.
  • If a name isn’t on this page, it doesn’t exist: don’t use it (a missing identifier resolves to undefined / renders nothing).
  • Never invent or guess a name; use only identifiers that appear verbatim here or that you verified against ground truth and added here.
  • Scope: exhaustive for comments + notifications + core; other features summarized.
Props lists the names, types, and defaults of every prop. This file owns the layer above that: what each behavior-bearing prop actually does, how props combine, what the SDK does with no prop at all, the dialog state machine, which variant scopes which context, and who owns positioning. When a requirement reads “make the dialog show the first reply and the last reply” or “open the composer collapsed,” this is the file that says whether a prop achieves it and exactly what renders. Read this alongside Component config (the layout/mode prop reference) and Feature flags (what’s on vs off by default). For the approaches a behavior can’t reach, see wireframes, primitives, and headless.

Per-component behavior: every prop (exhaustive)

The cross-cutting behaviors below (state machine, variant scoping, positioning, the headline interaction matrices) live in this file. The complete, per-prop behavior of every component: each prop’s runtime effect + default + interactions: is split per component. These files cover every prop in Props, plus source-only props/events that the Props page omits (so they are a superset, not strictly 1:1). On any default/behavior conflict, these files win (they are source-derived): Event behavior (when each fires + payload + gotcha) is in Events; data fields in Data models.

Default behaviors (no prop needed)

These are the SDK’s out-of-the-box behaviors. You do not pass a prop to get them: they are the baseline a prop modifies. Verified against the comment-dialog component state.

Prop-interaction matrix

For each behavior-bearing prop: what it does, its default, and how it combines with related props.

collapsedComments × collapsedRepliesPreview (the exact rendered reply structure)

These two props control which thread cards render. They are the most misunderstood pair, so here is the exact rendered structure, confirmed against the threads-rendering logic. The thread renderer decides per-comment-index whether the card shows, using three signals: is the dialog selected, showAllComments (default true), and collapsedRepliesPreview. The rule:
  • Dialog selected + showAllComments true (the default): every comment renders. Full thread.
  • Dialog selected + showAllComments false (this is what collapsedComments produces): only the first comment (index 0) and the last comment (index length-1) render. The hidden middle is replaced by a single “N more replies” divider (the velt-comment-dialog-more-reply element) between them.
  • Dialog NOT selected + collapsedRepliesPreview true: the same first + “N more replies” + last teaser renders before selection, so clicking “more replies” expands the thread in one step.
  • Dialog NOT selected + no preview (default): only the first comment renders.
So the exact collapsed structure is:
Critical absence: first + second + last is NOT achievable with props. The collapsed view is strictly index === 0 || index === length - 1. There is no prop to show “first reply, second reply, and last reply,” nor “first N replies.” If a requirement needs a different collapse pattern (first three, first + newest two, etc.), props cannot do it: climb to a thread-card / threads wireframe and render the cards yourself, or build the list with primitives. maxReplyAvatars (default 3) only caps avatars on the reply row, not which comment cards render: do not confuse it with reply count.

sortBy × sortOrder

sortBy picks the field ('createdAt' | 'lastUpdated'); sortOrder picks the direction ('asc' | 'desc'). They are applied together as one ordering of the comment list inside the dialog and the inline section. On the inline section, the older sortData ('asc'|'desc'|'none') is deprecated in favor of this pair: pass sortBy + sortOrder instead. With neither set, comments render in their natural (creation) order. They affect ordering only; they do not hide/collapse cards (that’s the section above).

visibilityOptions (requires isPrivateCommentsEnabled)

visibilityOptions (default false) shows the per-comment visibility picker (public / private / team). It is gated: the visibility banner only renders when the SDK’s private-comments capability (isPrivateCommentsEnabled) is also on. Turning on visibilityOptions without private comments enabled at the SDK/config level shows nothing. privateCommentMode is the related mode flag for making comments private by default. Treat visibilityOptions as the UI affordance and the private-comments capability as the prerequisite.

shadowDom vs dialogShadowDom (and the per-surface flags)

shadowDom is the master wrap for VeltComments; the per-surface flags (dialogShadowDom, pinShadowDom, textCommentToolShadowDom, …) default true and isolate each surface independently. Combination rule: to style a specific surface with your own selector CSS you must turn off the shadow DOM that wraps it: e.g. dialogShadowDom={false} to reach dialog internals, pinShadowDom={false} to reach the pin. Setting master shadowDom={false} drops isolation everywhere (the broad “I want to style with CSS” switch: rule R6). A per-surface true keeps that one surface isolated even if you’ve set others off. See CSS classes for what becomes selectable once isolation is off.

defaultMinimalFilter (overridden by saved state)

defaultMinimalFilter sets the sidebar’s initial quick-filter ('all' | 'unread' | 'resolved' | 'open' | 'assignedToMe' | … | null). Saved session state wins: the sidebar restores the user’s last-used filters from session storage on open, so defaultMinimalFilter only takes effect when there is no saved filter for that session. It is a first-run default, not a forced value. To force a filter on every open regardless of history, set values programmatically (setCommentSidebarFilters, see Component config) rather than relying on this prop.

embedMode / pageMode (sidebar)

  • embedMode renders the sidebar inline inside your layout instead of as a docked overlay. Note the type differs by version: boolean on VeltCommentsSidebar (V1), string on VeltCommentsSidebarV2: match the type to your component. It composes with position (which side, when not embedded) and floatingMode (float vs dock); embedding makes position/floatingMode moot because you own the placement.
  • pageMode switches to page-level comments: no pins on the DOM, and the composer lives at the sidebar level (use pageModePlaceholder for its placeholder and pageModeComposerVariant to wireframe it). This is the “comment on the whole page/doc” model, as opposed to the default anchored-pin model. pageMode and pin-based commenting are mutually exclusive surfaces.

fullExpanded vs default

fullExpanded (default false) forces threads to render fully expanded: every comment, message text not truncated. It is the opposite end from collapsedComments. Precedence: fullExpanded expands the thread; collapsedComments collapses it: don’t set both. With neither, the dialog uses the default (first-only until selected, all on select). messageTruncation (+ messageTruncationLines, default 4) is a separate axis: it truncates long single messages with show-more/less, independent of how many cards show.

commentPlaceholder vs replyPlaceholder priority

The composer’s placeholder resolves in this exact order (verified):
  1. Generic placeholder (input, then config-level): overrides everything if set.
  2. Otherwise selected by thread state: no comments yet → commentPlaceholder; thread already has comments → replyPlaceholder (a reply is being written).
  3. Falls through to a built-in default that varies with userMentions: with mentions on, “Comment or add others with @” / “Reply or add others with @”; with mentions off, “Write a comment…” / “Write a reply…”.
So commentPlaceholder and replyPlaceholder never both apply to one composer at one time: the thread’s comment count decides which is live. Edit mode uses its own chain (editPlaceholdereditCommentPlaceholder / editReplyPlaceholder, again with generic placeholder on top).

Dialog state machine

The pin dialog moves through these states. Both the dialog selection state and the composer open 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 → selected: clicking the pin (or dialogOnHover / dialogOnTargetElementClick, per props). Resets the reply-expanded teaser flag so re-opening shows the teaser again.
  • selected → expanded: automatic when expandOnSelection is true (the default). When expandOnSelection={false} (a sidebar option), selection does not expand: the dialog stays a first+last preview and the composer stays hidden: selection-without-expansion.
  • any → unselected: deselect, close, or forceCloseAllOnEsc (Esc closes all open dialogs when that prop is on).
Composer open state (independent, per instance):
  • collapsed → open: focusing/clicking the composer. With composerMode: 'default' (default) the composer starts collapsed; with composerMode: 'expanded' it starts open and never collapses.
  • open → recording: a recorder config is active for this instance (audio/video/screen). Recording state takes precedence over open in the rendered UI.
  • open → collapsed: on submit or programmatic clear (text, attachments, recordings, tagged users all reset).

Variant → context scoping

The same comment dialog renders in multiple contexts, and each context has its own variant prop so you can wireframe one without touching the others. This is the key to “I want the sidebar-opened dialog to look different from the floating pin dialog.” Customize one context without affecting the others: register a named wireframe variant (<VeltCommentDialogWireframe variant="myDialog">…) and point only the relevant variant prop at it. Setting dialogVariant="sidebarDialog" changes the sidebar-opened dialog while the floating pin dialog keeps the default look (and vice versa). If you set the base variant on VeltComments, you change the floating pin dialog only: the sidebar/focused/page contexts keep their defaults unless you also set their variant props. This is why “the dialog changed in the sidebar but not on pins” (or the reverse) is almost always a case of setting the wrong variant prop for the context.

Positioning ownership (anchored surfaces)

Verified fact: the host (VeltComments) owns pin placement and the dialog’s overlay anchoring. The comment-dialog component does not take a position or coordinate input and does not place itself: it only emits an updateOverlayPosition output (an @Output, no payload) to request that the host re-anchor the overlay when the dialog’s size changes (composer expands, recording starts, pin selection changes). The host listens and repositions. There is no position/x/y/target-coordinate input on the dialog; targetElementId names an anchor element id, not a coordinate, and the actual placement math lives in the host. Consequence: what each approach forfeits: Unsupported by this reference: the public API surface documented here does not expose enough to anchor a custom (primitive/headless) dialog at a Velt pin: i.e. to read the pin’s resolved screen coordinates and subscribe to re-anchor requests for your own element. Do not claim a custom dialog can be pinned to Velt’s pin position; if a requirement needs that, flag it as not verified rather than promising it. Surface classification (use this when routing a requirement: see Decision tree): Anchored surfaces are why wireframes are the cheapest customization for the dialog/pin/bubble: you inherit Velt’s positioning. The moment you drop to primitives or headless for an anchored surface, you take over positioning: budget for it.