# TextSelectionToolbar

> The toolbar that floats over text someone selects, with actions on that text such as Quote, Copy and Comment, in a message, an Artifact or any other readable text.

Source: https://raft-design.botiverse.build/components/text-selection-toolbar. Generated from the current release; where it differs from what you remember, this page is right.

`TextSelectionToolbar` floats over text the person selects, with actions on that text such as Quote, Copy and Comment. It is the same toolbar wherever text can be selected: in a message, in an Artifact, in any other text people read.

```tsx
import {
  AddCommentIcon,
  Text,
  TextSelectionToolbar,
  TextSelectionToolbarButton,
  TextSelectionToolbarSeparator,
} from "@botiverse/raft-ui";
import { Copy, Quote } from "lucide-react";
import { useCallback, useState } from "react";

export function TextSelectionToolbarContent() {
  const [last, setLast] = useState("");
  const quote = useCallback(() => setLast("Quoted"), []);
  const copy = useCallback(() => setLast("Copied"), []);
  const comment = useCallback(() => setLast("Comment started"), []);

  return (
    <div className="flex flex-col items-start gap-3">
      <TextSelectionToolbar aria-label="Selected text">
        <TextSelectionToolbarButton onClick={quote}>
          <Quote />
          Quote
        </TextSelectionToolbarButton>
        <TextSelectionToolbarButton onClick={copy}>
          <Copy />
          Copy
        </TextSelectionToolbarButton>
        <TextSelectionToolbarSeparator />
        <TextSelectionToolbarButton onClick={comment}>
          <AddCommentIcon />
          Comment
        </TextSelectionToolbarButton>
      </TextSelectionToolbar>
      <Text.Sans color="muted" size="small" role="status">
        {last}
      </Text.Sans>
    </div>
  );
}

export function TextSelectionToolbarComposition() {
  return (
    <div className="flex size-full items-center justify-center px-3 py-4">
      <TextSelectionToolbarContent />
    </div>
  );
}
```

## When to use

| Situation                                     | Use                                                                          |
| --------------------------------------------- | ---------------------------------------------------------------------------- |
| Actions on text the person selected           | TextSelectionToolbar                                                         |
| Actions on a message under the pointer        | The message's hover toolbar ([MessageItem](https://raft-design.botiverse.build/components/message-item.md)) |
| Actions on several selected messages or tasks | [SelectionToolbar](https://raft-design.botiverse.build/components/selection-toolbar.md)                |
| A list of choices that opens from a control   | [DropdownMenu](https://raft-design.botiverse.build/components/dropdown-menu.md)                            |

## Anatomy

- `TextSelectionToolbar`: the floating frame. Required.
- `TextSelectionToolbarButton`: one action, an icon and a label.
- `TextSelectionToolbarSeparator`: a line between groups of actions.
- `createTextSelectionAnchor(selection)`: the anchor that places the toolbar on the selected text, for `PopoverContent`'s `anchor`.

## Look

It is built like a message's hover toolbar, so the two read as one kind of thing. The one difference is that its buttons have text labels, where the hover toolbar's are icons alone:

- It is 36px high on a touch screen, around 28px buttons with `radius-chip`, and 42px with a mouse, around buttons at the control height (34px) with `radius-control` (stdrc, 2026-11-05): `radius-container` with its hairline drawn inside (`ring-inset`), the buttons 4px from the frame and 4px apart.
- Its buttons have text labels after 16px icons in `foreground-muted`. On hover they take the medium tier, `fill-medium`, not the `brand-soft` of a menu row: a toolbar's actions are buttons, not menu rows ([States](https://raft-design.botiverse.build/foundations/states.md)).
- `TextSelectionToolbarSeparator` is 20px high, with the toolbar's 4px on each side.

## Behavior

- It anchors to the line where the selection's focus end is, where the person let go, not to the whole range: a whole-line or multi-paragraph selection spans the text column, and the middle of that is nowhere near the pointer. Build the anchor with `createTextSelectionAnchor(selection)` and pass it to `PopoverContent`'s `anchor`; it measures the line each time it is read, so the toolbar follows the text when it scrolls.
- It stays inside the viewport: above the line by default, it flips below when there is no room and shifts along the line near a side edge.

## Content

- Write labels in sentence case. They never wrap: Chinese text can break between any two characters, so a toolbar squeezed at the edge of the window broke a two-character label onto two lines.

## Accessibility

- `TextSelectionToolbar` wraps Base UI `Toolbar`; give it an `aria-label`. Its buttons are Base UI toolbar buttons: the arrow keys move between them.

## API

### TextSelectionToolbar

The toolbar that floats over selected text, with actions such as Quote, Copy and Comment: in a message, in an Artifact, anywhere text can be selected. Place it with `createTextSelectionAnchor` in a Popover.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `disabled` | `boolean` |  |  |
| `loopFocus` | `boolean` | `true` | If `true`, using keyboard navigation will wrap focus to the other end of the toolbar once the end is reached. |
| `orientation` | `BaseToolbar.Orientation` | `'horizontal'` | The orientation of the toolbar. |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### TextSelectionToolbarButton

One action on the selected text: a 28px button with a 16px icon and a sentence-case label that never wraps.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `disabled` | `boolean` | `false` | When `true` the item is disabled. |
| `focusableWhenDisabled` | `boolean` | `true` | When `true` the item remains focusable when disabled. |
| `nativeButton` | `boolean` | `true` | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

### TextSelectionToolbarSeparator

A 20px line between groups of actions in the toolbar.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `orientation` | `BaseToolbar.Orientation` | `'horizontal'` | The orientation of the separator. Defaults to the opposite of the toolbar's orientation, so a horizontal toolbar renders vertical separators. |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

## Changes

- 0.52.0 (Added): **TextSelectionToolbar** (+ `TextSelectionToolbarButton`, `TextSelectionToolbarSeparator`, `createTextSelectionAnchor`): the generic name for the toolbar on selected text. The `MessageTextSelectionToolbar*` names stay as deprecated aliases.
