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

# Wireframes Overview

> Customize Velt UI structure with wireframes while preserving Velt behavior and data wiring.

Keep all of Velt's behavior **and data wiring**, but supply your own HTML layout. You decompose a Velt component into named **slots** (header, thread card, composer, …) and fill each slot with your markup. Velt fetches the data, loops the threads and comments, and wires each slot's behavior; you only lay out the slots. **This is the default for structural customization**, and it's less work than primitives, where you'd write the data plumbing yourself.

**Use it when** the design changes the structure of Velt's UI (custom header, reordered parts, custom thread card, custom empty state) while the features stay the same, and your custom parts are **non-interactive markup**. **Don't** when you need your own interactive components inside the UI (that's [primitives](/docs/ui-customization/primitives)) or you only need recoloring (that's [CSS](/docs/ui-customization/styling)). Still unsure? Run the [decision tree](/docs/ui-customization/decision-tree).

<Note>
  This page is the concepts and rules. The rest of the section:

  * [Setup Wireframes](/docs/ui-customization/wireframes/setup-wireframes): step-by-step setup with React and Other Frameworks examples.
  * [Layout Customization](/docs/ui-customization/wireframes/layout-customization): targeted vs full-tree overrides, variants, replace/remove/reorder recipes.
  * [Template Variables](/docs/ui-customization/template-variables): the complete `{…}` variable catalog per component.
  * [Conditional Templates](/docs/ui-customization/conditional-templates): show or hide parts with `velt-if`.
  * [Conditional Classes](/docs/ui-customization/conditional-classes): toggle CSS classes with `velt-class`.
  * [Action Components](/docs/ui-customization/wireframes/action-components): custom buttons, toggles, and select groups.

  Prefer automation? The [UI Customization Plugin](/docs/get-started/ui-customization-plugin) builds wireframe customizations from a Figma design and verifies them in a real browser.
</Note>

## The model

Two pieces work together:

1. **`<VeltWireframe>`**: an **invisible registry** (`display:none`). You put your wireframe templates inside it. **Use one per app**: more than one merges first-with-content-wins, which causes hard-to-debug conflicts.
2. **`Velt…Wireframe` slot components**: e.g. `VeltCommentDialogWireframe`, `VeltCommentsSidebarWireframe`, with nested static slots like `.Header`, `.Body`, `.ThreadCard`, `.Composer`. You fill these with your own markup.

Then, **separately**, you mount the normal feature component (`VeltComments`, `VeltCommentsSidebar`, `VeltCommentDialog`, …). It renders using the template you registered.

So a wireframe doesn't render anything by itself: it **registers a template** that the live feature component picks up.

## Quick example

Register a template inside the (single) `<VeltWireframe>` registry, then mount the live feature component as normal. Your own elements provide structure and visuals; the `Velt…Wireframe.X` slots are where Velt's behavior renders.

<Tabs>
  <Tab title="React / Next.js">
    ```tsx theme={null}
    import { VeltWireframe, VeltCommentDialogWireframe, VeltComments } from "@veltdev/react";

    <VeltWireframe>
      <VeltCommentDialogWireframe>
        <div className="vcd-shell">
          <VeltCommentDialogWireframe.Header>
            <header className="vcd-header">
              <VeltCommentDialogWireframe.Status />
              <VeltCommentDialogWireframe.ResolveButton />
              <VeltCommentDialogWireframe.CloseButton />
            </header>
          </VeltCommentDialogWireframe.Header>
          <VeltCommentDialogWireframe.Body />
          <VeltCommentDialogWireframe.Composer />
        </div>
      </VeltCommentDialogWireframe>
    </VeltWireframe>

    // elsewhere: the live component renders using the template above
    <VeltComments shadowDom={false} />
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```html theme={null}
    <velt-wireframe style="display:none;">
      <velt-comment-dialog-wireframe>
        <div class="vcd-shell">
          <velt-comment-dialog-header-wireframe>
            <header class="vcd-header">
              <velt-comment-dialog-status-wireframe></velt-comment-dialog-status-wireframe>
              <velt-comment-dialog-resolve-button-wireframe></velt-comment-dialog-resolve-button-wireframe>
              <velt-comment-dialog-close-button-wireframe></velt-comment-dialog-close-button-wireframe>
            </header>
          </velt-comment-dialog-header-wireframe>
          <velt-comment-dialog-body-wireframe></velt-comment-dialog-body-wireframe>
          <velt-comment-dialog-composer-wireframe></velt-comment-dialog-composer-wireframe>
        </div>
      </velt-comment-dialog-wireframe>
    </velt-wireframe>

    <!-- elsewhere: the live component renders using the template above -->
    <velt-comments shadow-dom="false"></velt-comments>
    ```
  </Tab>
</Tabs>

Full step-by-step setup, with Other Frameworks equivalents for every step: [Setup Wireframes](/docs/ui-customization/wireframes/setup-wireframes).

<Tip>
  **Slots take inputs too.** Some slots accept props, such as `Composer.ActionButton type="submit"`, `Composer.Input placeholder="..."`, and `ThreadCard.Reactions excludeReactionIds={[...]}`. The complete slot list, per-slot props, and every wireframe component are in [`Wireframe components`](/docs/ui-customization/reference/wireframe-components).
</Tip>

## The interactivity rule

Read this before you write any wireframe. It is the #1 source of wireframe bugs.

> **Inside a wireframe, your own React interactivity does NOT run. Behavior comes only from Velt's `Velt…Wireframe.X` slot components.**

When Velt renders a wireframe it **copies your slot markup** into its own render tree: it serializes your slot to HTML and re-instantiates only the `velt-*` slot elements inside it. The copy is plain DOM, which strips React listeners. So for markup you put in a slot:

| In your wireframe markup                                                                                | Survives into the live UI?                             |
| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| Static elements (`<div>`, `<span>`, `<header>`, icons)                                                  | ✅ yes                                                  |
| **Your UI-library components used as static presentation** (a `<Card>`, `<Badge>`, styled button shell) | ✅ yes: their **rendered markup + CSS classes** survive |
| CSS `className` / inline styles                                                                         | ✅ yes                                                  |
| `{…}` tokens (`velt-if`, `velt-class`, `velt-data`)                                                     | ✅ yes (Velt resolves them)                             |
| `Velt…Wireframe.X` slot components                                                                      | ✅ yes: **this is where behavior comes from**           |
| Your React `onClick`, `useState`, hooks                                                                 | ❌ **no: silently dead**                                |
| A UI-library component's **behavior** (its own click/state/effects)                                     | ❌ no: only its static markup renders                   |

**What to do instead:** want a working button? Use the **Velt slot** for it (`VeltCommentDialogWireframe.ResolveButton`, `.Options.Content.Delete`, `.Composer.ActionButton`, …). Your markup goes *inside* that slot as its appearance:

<Tabs>
  <Tab title="React / Next.js">
    ```tsx theme={null}
    {/* ✅ correct: the Velt slot provides the click behavior; your markup is the look */}
    <VeltCommentDialogWireframe.ResolveButton>
      <span className="my-icon-btn"><ResolveIcon /></span>
    </VeltCommentDialogWireframe.ResolveButton>

    {/* ❌ wrong: this onClick never runs in the rendered dialog */}
    <button onClick={() => doSomething()}>Resolve</button>
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```html theme={null}
    <!-- ✅ correct: the Velt slot provides the click behavior; your markup is the look -->
    <velt-comment-dialog-resolve-button-wireframe>
      <span class="my-icon-btn"><!-- your icon --></span>
    </velt-comment-dialog-resolve-button-wireframe>

    <!-- ❌ wrong: this listener never runs in the rendered dialog -->
    <button onclick="doSomething()">Resolve</button>
    ```
  </Tab>
</Tabs>

You *can* drop in your design-system components for their look, since markup and classes survive the clone, but their interactivity won't run. For interactive library components use [primitives](/docs/ui-customization/primitives); for behavior no slot provides, go [headless](/docs/ui-customization/headless).

<Note>
  **See it for yourself.** Put a `<button onClick={…}>` in a wireframe slot and open DevTools: it renders as **two** nodes. The hidden React original (`display:none`) still has its React props and a working handler. The **visible cloned copy** injected into the live dialog has no React props, and its handler never fires. The node users can actually click is the one without the handler.
</Note>

## Scoping: global vs scoped wireframes

Where you place a child wireframe changes **where it applies**:

* **Nested *inside* its parent wireframe → scoped to that parent's render.** It travels as part of the parent's cloned subtree, so it only customizes the child *as it appears inside that parent*. A `ThreadCard` layout placed inside `VeltCommentDialogWireframe` customizes thread cards **in the dialog**, not elsewhere.
* **Placed *directly* at the `<VeltWireframe>` root → global.** It registers under its own key and applies to that component **everywhere it renders** (dialog, sidebar, inline section, …).

```tsx theme={null}
<VeltWireframe>
  {/* SCOPED: this ThreadCard layout applies only inside the dialog */}
  <VeltCommentDialogWireframe>
    <VeltCommentDialogWireframe.Body>
      <VeltCommentDialogWireframe.Threads>
        <VeltCommentDialogWireframe.ThreadCard>{/* …custom… */}</VeltCommentDialogWireframe.ThreadCard>
      </VeltCommentDialogWireframe.Threads>
    </VeltCommentDialogWireframe.Body>
  </VeltCommentDialogWireframe>

  {/* GLOBAL: a ThreadCard registered at the root would apply to thread cards everywhere */}
</VeltWireframe>
```

**Why:** the registry is a flat map keyed by component name (plus optional `variant`), never by parent. The root scan registers only its **direct** children as global keys, so a nested child isn't registered globally: it rides inside the parent's clone. Collisions resolve first-with-content-wins, so a root definition is *not* overwritten by a nested one.

**Rule:** nest to scope, root-level to go global; don't register the same component both ways.

## Slot granularity

The slot tree is **very** fine-grained (hundreds of slots across the SDK). Inside a comment dialog alone you'll find composer → `Input` / `Attachments` / `AssignUser`; thread-card → `Avatar` / `Name` / `Time` / `Message` / `Options` / `Reactions`; options dropdown → `Edit` / `Delete`.

**You only fill the slots you care about: a slot you never declare falls back to Velt's default.** So you can do a tiny override (just the empty state) or a near-total rebuild (40+ slots across the dialog and sidebar).

<Warning>
  **Container slots are the exception.** The fallback rule holds for **leaf** slots. Declare a **structural/container** slot (a feature root like `velt-comments-sidebar-v2-wireframe`, or a parent like the sidebar `panel`/`header`) and **you own its whole child tree: structural children you don't declare disappear rather than falling back.** A sidebar root wireframe declaring *only* a custom empty-placeholder renders the empty state but **drops the search box, filter buttons, and list**. Fix: declare the full tree you want inside the container (`panel → header(search, filter) → list → empty-placeholder`). Override a *leaf* and the rest stays; override a *container* and you re-declare its children.
</Warning>

[List and repeater slots](#list-and-repeater-slots) are the other exception. The complete slot list per feature: [`Wireframe components`](/docs/ui-customization/reference/wireframe-components). Worked targeted-vs-full-tree examples: [Layout Customization](/docs/ui-customization/wireframes/layout-customization#ways-to-customize-layout).

## Variants

By default a component has **one** registered wireframe. Variants let you register several templates for the *same* component and choose which one renders by name, so one component can look different in different contexts (floating dialog vs sidebar row vs focused thread vs page-mode composer).

* **Register:** `variant="<name>"` on the `Velt…Wireframe`.
* **Select:** the matching prop on the live component (`variant`, `dialogVariant`, `focusedThreadDialogVariant`, `pageModeComposerVariant`). Which ones a given component accepts is listed per component in [`Props`](/docs/ui-customization/reference/props#part-2-all-other-components).
* **Fallback:** no matching variant means the base (no-variant) wireframe renders.

Create/use walkthroughs, including pre-defined variants: [Layout Customization → Variants](/docs/ui-customization/wireframes/layout-customization#variants).

## List and repeater slots

A few slots are **list/repeater containers**: the comments list, the presence avatar list, the reactions panel items, the activity-log list. These keep rendering Velt's own loop, so **your layout around them is ignored**.

* ❌ Wrapping a list slot in your own grid/flex layout, or adding sibling markup inside it, won't take effect.
* ✅ **Customize the repeated *item* instead.** Velt passes the item template straight to the child component, so you restructure each row through its own item wireframe.

```tsx theme={null}
{/* The LIST container keeps Velt's loop: your wrapping layout here is ignored.
    Customize the per-row look via the item/child slot it renders. */}
<VeltCommentsSidebarWireframe.List>
  <VeltCommentsSidebarWireframe.List.Item>{/* ← customize the ROW here */}</VeltCommentsSidebarWireframe.List.Item>
</VeltCommentsSidebarWireframe.List>
```

**Rule:** if a slot represents a *list of things*, don't relayout the list, restructure the item. If you truly need a custom list layout or virtualization, that's a signal for [primitives](/docs/ui-customization/primitives), where you own the loop.

## Tokens: live data in your markup

Wireframe markup reads Velt's live state through `{…}` tokens. There are three things you can do with one:

| Token        | What it does                        | Reference                                                                     |
| ------------ | ----------------------------------- | ----------------------------------------------------------------------------- |
| `velt-if`    | Show or hide a block on a condition | [Conditional Templates](/docs/ui-customization/conditional-templates)              |
| `velt-data`  | Print a live value as text          | [Template Variables](/docs/ui-customization/template-variables#display-a-variable) |
| `velt-class` | Toggle CSS classes on a condition   | [Conditional Classes](/docs/ui-customization/conditional-classes)                  |

Variable names are a **fixed set** (`{user}`, `{annotation}`, `{comment}`, `{commentIndex}`, `{noCommentsFound}`, `{darkMode}`, …). A name outside the catalog resolves to `undefined`, so never invent one: the full list is in [`Template Variables`](/docs/ui-customization/template-variables).

## Page mode

"Page mode" renders the comments **sidebar anchored to elements on your page** (one thread per form question, for example), with a per-element comment-count bubble and a page-mode composer. It's still just wireframes: fill the sidebar, thread, composer, and bubble-count slots and Velt keeps the behavior.

Page mode usually pairs with **context**: attaching your domain data (the question id or title) to each comment and reading it back in the dialog or composer. See [`Context`](/docs/ui-customization/context).

## What it can and can't do

| ✅ Wireframes can                                                 | ❌ Wireframes can't                                                                                                      |
| ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| Give Velt's UI any layout/structure you want                     | Run your React handlers/state/hooks inside slots                                                                        |
| Add non-interactive custom markup around Velt's parts            | Host live UI-library components (interactivity is stripped)                                                             |
| Conditionally render via `velt-if` and show data via `velt-data` | Change Velt's *behavior* (slots give you Velt's behavior, not custom)                                                   |
| Override only the slots you care about                           | Relayout a **list/repeater** slot: customize its **item** instead ([list and repeater slots](#list-and-repeater-slots)) |
| Customize each row via its item slot                             | Be split across multiple `<VeltWireframe>` roots                                                                        |

## Where to look things up

* **Slots & slot props:** [`Wireframe components`](/docs/ui-customization/reference/wireframe-components) (every wireframe + full slot trees) and the overview in [`Component catalog`](/docs/ui-customization/reference/component-catalog).
* **Variables/tokens:** the full `{…}` catalog in [`Template Variables`](/docs/ui-customization/template-variables); condition syntax in [`Conditional Templates`](/docs/ui-customization/conditional-templates) and [`Conditional Classes`](/docs/ui-customization/conditional-classes).
* **Stateful CSS classes** (to style state without a slot): [`CSS classes`](/docs/ui-customization/reference/css-classes).
* **Props on the live component** (variant selection, `shadowDom`, feature toggles): [`Props`](/docs/ui-customization/reference/props).

## Troubleshooting

Common wireframe symptoms, in [`Debugging`](/docs/ui-customization/debugging):

* ["My wireframe renders nothing"](/docs/ui-customization/debugging#my-wireframe-renders-nothing)
* ["My wireframe markup renders inline on the page"](/docs/ui-customization/debugging#my-wireframe-markup-renders-inline-on-the-page-and-the-component-still-shows-defaults)
* ["My wireframe's empty state works, but the header, search, and list vanished"](/docs/ui-customization/debugging#my-wireframes-empty-state-works-but-the-header-search-and-list-vanished)
* ["My wireframe applies in the wrong places"](/docs/ui-customization/debugging#my-wireframe-applies-in-the-wrong-places-or-not-where-i-want)
* ["A button, onClick, or hook inside my wireframe does nothing"](/docs/ui-customization/debugging#a-button-onclick-or-hook-inside-my-wireframe-does-nothing)
* ["`velt-data` shows blank, or `velt-if` never matches"](/docs/ui-customization/debugging#velt-data-shows-blank-or-velt-if-never-matches)
* ["Default styling is still there even though I wireframed it"](/docs/ui-customization/debugging#default-styling-is-still-there-even-though-i-wireframed-it)

## Checklist

* [ ] Exactly **one** `<VeltWireframe>` in the app.
* [ ] The live feature component (`VeltComments` / `VeltCommentsSidebar` / `VeltCommentDialog`) is **mounted** in addition to the wireframe.
* [ ] No React `onClick`/`useState`/hooks inside slot markup: interactivity comes from `Velt…Wireframe.X` slots only.
* [ ] For list/repeater slots, customized the **item**, not the container layout ([list and repeater slots](#list-and-repeater-slots)).
* [ ] Only real slot names ([`Wireframe components`](/docs/ui-customization/reference/wireframe-components)) and real `{…}` variables ([`Template Variables`](/docs/ui-customization/template-variables)).
* [ ] `shadowDom={false}` if you style the result.
* [ ] Decided scope per child wireframe: nested = scoped, root = global ([scoping](#scoping-global-vs-scoped-wireframes)).
* [ ] Unfilled slots intentionally left to Velt defaults.
