# SelectionToolbar
URL: /docs/primitives/selection-toolbar

A floating toolbar that appears when text is selected within a message.

> For AI agents: a documentation index is available at [llms.txt](/llms.txt). Use `.md` for canonical markdown pages; `.mdx` is kept as a backwards-compatible alias on supported URL paths.

The SelectionToolbar primitive is a floating toolbar that appears when the user selects text within a message. It lets users quote selected text into the composer. Styling and action layout are fully customizable.

Choose one:

**Preview**

\[interactive preview omitted]

**Code**

```
import { SelectionToolbarPrimitive } from "@assistant-ui/react";
import { QuoteIcon } from "lucide-react";

function FloatingSelectionToolbar() {
  return (
    <SelectionToolbarPrimitive.Root className="flex items-center gap-1 rounded-lg border bg-popover px-1 py-1 shadow-md">
      <SelectionToolbarPrimitive.Quote className="flex items-center gap-1.5 rounded-md px-2.5 py-1 text-popover-foreground text-sm hover:bg-accent">
        <QuoteIcon className="size-3.5" />
        Quote
      </SelectionToolbarPrimitive.Quote>
    </SelectionToolbarPrimitive.Root>
  );
}
```

## Quick Start

A minimal selection toolbar:

```
import { SelectionToolbarPrimitive } from "@assistant-ui/react";

<SelectionToolbarPrimitive.Root>
  <SelectionToolbarPrimitive.Quote>
    Quote
  </SelectionToolbarPrimitive.Quote>
</SelectionToolbarPrimitive.Root>
```

`Root` renders a `<div>` inside a portal, positioned above the text selection. `Quote` renders a `<button>` that copies the selected text into the composer as a quote.

> [!info]
>
> Runtime setup: primitives require runtime context. Wrap your UI in `AssistantRuntimeProvider` with a runtime (for example `useLocalRuntime(...)`). See [Pick a Runtime](/docs/runtimes/pick-a-runtime).

## Core Concepts

### Automatic Positioning

`Root` renders via a portal to `document.body`. In the current implementation, it positions itself with `position: fixed`, `top: rect.top - 8px`, `left: rect.left + rect.width / 2`, `transform: translate(-50%, -100%)` (placing it centered above the selection), and `z-index: 50`.

### Single-Message Validation

The toolbar only appears when the selection is entirely within a single message. Cross-message selections are ignored. Detection works by checking for `data-message-id` attributes on ancestor elements, which `MessagePrimitive.Root` sets automatically.

`Root` returns `null` when there is no valid selection (collapsed selection, empty text, or no single-message match).

### Quote Regions

By default, any selected text inside a message can be quoted. For rich messages with tool cards, attachments, or custom controls, add `data-aui-quote-selectable` to the element that contains the message text. Once a message has a quote-selectable region, selections must stay inside the same marked region for the toolbar to appear.

```
<MessagePrimitive.Root>
  <div data-aui-quote-selectable="">
    <MessagePrimitive.Parts />
  </div>
  <ToolCard />
</MessagePrimitive.Root>
```

Set `data-aui-quote-selectable="false"` to exclude an element instead: on the message root it disables quoting for the entire message, and inside a quotable region it carves that subtree out. The nearest marker wins, and any value other than `"false"` (including empty) marks a quotable region.

Apps know the message role at render time, so a role gate is a conditional attribute:

```
const UserMessage = () => (
  <MessagePrimitive.Root data-aui-quote-selectable="false">
    <MessagePrimitive.Parts />
  </MessagePrimitive.Root>
);
```

### Quote Action

The `Quote` button does two things on click:

1. Calls `composer.setQuote({ text, messageId })` to populate the composer with the selected text as a quoted snippet
2. Clears the browser selection via `window.getSelection()?.removeAllRanges()`

The composer then shows the quoted text, and the user can type a follow-up message around it.

### Dismissal

The toolbar hides automatically when:

- The user scrolls
- The selection collapses (click away or arrow keys)
- The selection is cleared programmatically (e.g., after quoting)

Clicking the toolbar itself does not dismiss the selection. `Root` calls `preventDefault` on `mousedown` to preserve it.

### Placement in Thread

The SelectionToolbar must be placed inside `ThreadPrimitive.Root` but **outside** `ThreadPrimitive.Viewport`. It positions itself via a portal based on the browser selection, independent of the scroll container:

```
<ThreadPrimitive.Root>
  <ThreadPrimitive.Viewport>
    <ThreadPrimitive.Messages ... />
  </ThreadPrimitive.Viewport>
  <SelectionToolbarPrimitive.Root>
    <SelectionToolbarPrimitive.Quote>Quote</SelectionToolbarPrimitive.Quote>
  </SelectionToolbarPrimitive.Root>
  <ComposerPrimitive.Root>...</ComposerPrimitive.Root>
</ThreadPrimitive.Root>
```

## Parts

### Root

Floating toolbar container that appears for a valid text selection. Renders a `<div>` element unless `asChild` is set.

```
<SelectionToolbarPrimitive.Root className="flex items-center gap-1 rounded-lg border bg-popover px-1 py-1 shadow-md">
  <SelectionToolbarPrimitive.Quote>Quote</SelectionToolbarPrimitive.Quote>
</SelectionToolbarPrimitive.Root>
```

### Quote

Quotes the currently selected text into the composer. Renders a `<button>` element unless `asChild` is set.

```
<SelectionToolbarPrimitive.Quote className="rounded-md px-2.5 py-1 text-sm hover:bg-accent">
  Quote
</SelectionToolbarPrimitive.Quote>
```

## Patterns

### Basic Toolbar with Quote

```
<SelectionToolbarPrimitive.Root className="flex items-center gap-1 rounded-lg border bg-popover px-1 py-1 shadow-md">
  <SelectionToolbarPrimitive.Quote className="flex items-center gap-1.5 rounded-md px-2.5 py-1 text-sm text-popover-foreground hover:bg-accent">
    <QuoteIcon className="size-3.5" />
    Quote
  </SelectionToolbarPrimitive.Quote>
</SelectionToolbarPrimitive.Root>
```

### Toolbar with Multiple Actions

You can add custom buttons alongside `Quote` for additional actions:

```
<SelectionToolbarPrimitive.Root className="flex items-center gap-1 rounded-lg border bg-popover px-1 py-1 shadow-md">
  <SelectionToolbarPrimitive.Quote className="flex items-center gap-1.5 rounded-md px-2.5 py-1 text-sm hover:bg-accent">
    <QuoteIcon className="size-3.5" />
    Quote
  </SelectionToolbarPrimitive.Quote>
  <button
    className="flex items-center gap-1.5 rounded-md px-2.5 py-1 text-sm hover:bg-accent"
    onClick={() => navigator.clipboard.writeText(window.getSelection()?.toString() ?? "")}
  >
    <CopyIcon className="size-3.5" />
    Copy
  </button>
</SelectionToolbarPrimitive.Root>
```

## Relationship to Components

There is no pre-built shadcn component for SelectionToolbar. See the [Quoting guide](/docs/guides/quoting) for the full workflow including composer quote display and dismiss.

## API Reference

For full prop details on every part, see the [SelectionToolbarPrimitive API Reference](/docs/api-reference/primitives/selection-toolbar).

Related:

- [Quoting Guide](/docs/guides/quoting)
- [ComposerPrimitive API Reference](/docs/api-reference/primitives/composer)