Toolbar
Grouped controls for rich editors and tool palettes with roving focus, toggle groups, links, and separators.
Installation
The components are exported from @by/experience-system. Add the package with your package manager:
pnpm add @by/experience-systemIn this monorepo, depend on the workspace package (for example via workspace:* or your catalog) so imports resolve to packages/experience-system.
Composition
Build a Toolbar from ToolbarToggleGroup / ToolbarToggleItem, ToolbarGroup / ToolbarGroupButton, ToolbarButton, ToolbarLink, ToolbarSeparator, ToolbarZoneSplitter, and optionally ToolbarOverflowItem for non-primitive controls:
Toolbar
├── ToolbarToggleGroup
│ └── ToolbarToggleItem …
├── ToolbarGroup
│ └── ToolbarGroupButton …
├── ToolbarSeparator (optional)
├── ToolbarZoneSplitter (optional, flex-grow zone spacer)
├── ToolbarButton | ToolbarLink …
├── ToolbarOverflowItem (optional, wraps non-primitive controls)
└── …Other primitives with asChild triggers (for example DropdownMenu) can be composed inside the toolbar per Radix composition. See Radix Toolbar.
Usage
import {
Toolbar,
ToolbarButton,
ToolbarGroup,
ToolbarGroupButton,
ToolbarOverflowItem,
ToolbarSeparator,
ToolbarToggleGroup,
ToolbarToggleItem,
ToolbarZoneSplitter,
} from '@by/experience-system';All toolbar parts are client components ('use client'). Use them inside a Client Component or a dynamic import when using the Next.js App Router.
<Toolbar aria-label="Actions" orientation="horizontal" loop>
<ToolbarToggleGroup type="single" defaultValue="left" aria-label="Align">
<ToolbarToggleItem value="left" aria-label="Left">
L
</ToolbarToggleItem>
<ToolbarToggleItem value="right" aria-label="Right">
R
</ToolbarToggleItem>
</ToolbarToggleGroup>
<ToolbarSeparator />
<ToolbarButton type="button">Save</ToolbarButton>
</Toolbar>Examples
Overview
Multiple and single ToolbarToggleGroup regions, a ToolbarLink, and a ToolbarButton (same preview as at the top of the page).
Toggle groups
Two groups only—style (multiple) and alignment (single).
Groups
ToolbarGroup and ToolbarGroupButton group related actions — Undo/Redo, Cut/Copy/Paste, Zoom In/Out, and Upload/Download — so each set overflows as a single unit. ToolbarToggleItem blocks (Bold/Italic/Underline) still carry selection state alongside the non-toggle groups. Use variant="outline" for a bordered segmented container or variant="ghost" (default) for a flat arrangement. Each ToolbarGroupButton inside the group is a plain action button (no toggle state). Omit icon (default false) for icon + text labels; set icon for icon-only slots.
Link
ToolbarButton with ToolbarLink items for secondary navigation.
In card
Place the toolbar in CardAction for header-level actions, or in CardHeader when it controls body content. Narrow the card to see overflow in both placements.
Toolbar in card header
Place the toolbar in CardAction so it aligns to the trailing edge of the header grid.
Toolbar in card body
Place the toolbar inside CardContent when it controls the body content — for example, a rich-text or chart surface.
Vertical toolbar
A vertical Toolbar with icon-only ToolbarButton items. Each button uses menuItem and visibility so items collapse into the ellipsis menu when space is constrained along the toolbar axis.
Zone splitter
Two ToolbarZoneSplitter elements divide the toolbar into three zones: history on the left, formatting in the center, and transfer/save actions on the right. Splitters absorb free space and do not participate in overflow collapse.
Responsive — mixed content
Toggle groups and labeled action buttons share the same overflow container. The entire toggle-group block collapses as a single unit. A ToolbarZoneSplitter separates formatting controls on the left from search and action buttons on the right. ToolbarOverflowItem wraps the search Input. Use visibility="always-visible" to pin items in JSX order; use visibility="always-hidden" to keep an item in the overflow menu only.
Responsive — adaptive search
Use ToolbarAdaptiveItem for three-tier responsive slots: expanded (wide toolbar), collapsed (compact inline fallback), and optional menuItem (ellipsis menu). Set collapseBelow to the toolbar width (or height when vertical) at which the slot switches from expanded to collapsed. Use visibility="always-visible" when the compact form should stay inline while sibling items collapse first.
This differs from the mixed-content example, where search collapses entirely into a DropdownMenuItem with no inline icon fallback.
Floating toolbar
A compact formatting toolbar hosted inside a Card — useful for floating palettes and contextual tool surfaces. Overflow collapses items into the ellipsis menu when the container is constrained.
API Reference
The tables below mirror the Radix UI Toolbar API. Subsection titles match @by/experience-system exports. ToolbarButton and ToolbarLink layer Button styles on Radix Toolbar.Button and Toolbar.Link. ToolbarButton, ToolbarSeparator, ToolbarToggleGroup, ToolbarToggleItem, ToolbarOverflowItem, and ToolbarAdaptiveItem all extend ToolbarOverflowableProps (menuItem, visibility) for responsive overflow behaviour.
ToolbarOverflowableProps
Shared interface extended by every toolbar primitive that participates in overflow.
| Prop | Type | Default |
|---|---|---|
menuItem | React.ReactNode | — — content rendered inside the overflow dropdown when this item is hidden |
visibility | 'always-visible' | 'always-hidden' | undefined | undefined |
'always-visible'— keeps the item at its declared position among siblings; it is never collapsed into the overflow menu.'always-hidden'— always places the item in the overflow menu and never renders it in the toolbar.undefined(default) — normal responsive behaviour; item collapses when the toolbar runs out of space.
Toolbar
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
orientation | horizontal | vertical | horizontal |
dir | ltr | rtl | — |
loop | boolean | true |
overflow | boolean | true |
overflowTriggerLabel | string | 'More options' |
overflowTriggerSize | Button size values | 'md' |
overflowTriggerIcon | boolean | true |
| Data attribute | Values |
|---|---|
data-slot | toolbar |
data-orientation | horizontal | vertical |
ToolbarButton
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
variant | Button variant values | outline |
size | Button size values | sm |
color | Button color values | neutral |
icon | boolean | false |
menuItem | React.ReactNode | — |
visibility | 'always-visible' | 'always-hidden' | undefined | undefined |
| Data attribute | Values |
|---|---|
data-slot | toolbar-button |
data-color | mirrors color |
data-orientation | horizontal | vertical |
ToolbarLink
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
size | Button size values | sm |
| Data attribute | Values |
|---|---|
data-slot | toolbar-link |
data-orientation | horizontal | vertical |
ToolbarSeparator
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
menuItem | React.ReactNode | — |
visibility | 'always-visible' | 'always-hidden' | undefined | undefined |
| Data attribute | Values |
|---|---|
data-slot | toolbar-separator |
data-orientation | horizontal | vertical |
ToolbarToggleGroup
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
type | single | multiple | — (required) |
value | string or string[] | — |
defaultValue | string or string[] | — |
onValueChange | (value: string | string[]) => void | — |
disabled | boolean | false |
variant | outline | ghost | ghost |
size | sm | md | lg | sm |
menuItem | React.ReactNode | — |
visibility | 'always-visible' | 'always-hidden' | undefined | undefined |
| Data attribute | Values |
|---|---|
data-slot | toolbar-toggle-group |
data-orientation | horizontal | vertical |
ToolbarToggleItem
| Prop | Type | Default |
|---|---|---|
asChild | boolean | false |
value | string | — (required) |
disabled | boolean | — |
variant | outline | ghost | From group context |
size | sm | md | lg | From group context |
icon | boolean | false |
| Data attribute | Values |
|---|---|
data-slot | toolbar-toggle-item |
data-icon-only | Present when icon is true |
data-state | on | off |
data-disabled | Present when disabled |
data-orientation | horizontal | vertical |
ToolbarGroup
A container that groups related action buttons with the same visual chrome as ToolbarToggleGroup but carries no selection state. The entire group collapses into the overflow menu as a single unit. Extends ToolbarOverflowableProps and all standard HTMLDivElement attributes.
| Prop | Type | Default |
|---|---|---|
variant | outline | ghost | ghost |
size | sm | md | lg | md |
orientation | horizontal | vertical | Inherited from Toolbar context |
menuItem | React.ReactNode | — |
visibility | 'always-visible' | 'always-hidden' | undefined | undefined |
| Data attribute | Values |
|---|---|
data-slot | toolbar-group |
data-orientation | horizontal | vertical |
ToolbarGroupButton
A non-toggle button styled as a segment within a ToolbarGroup. Uses the same sizing and chrome classes as ToolbarToggleItem but carries no pressed/checked state. Participates in toolbar keyboard navigation via the Radix Toolbar.Button primitive.
| Prop | Type | Default |
|---|---|---|
variant | outline | ghost | From ToolbarGroup context |
size | sm | md | lg | From ToolbarGroup context |
icon | boolean | false |
orientation | horizontal | vertical | From ToolbarGroup context |
color | Button color values | — |
disabled | boolean | — |
| Data attribute | Values |
|---|---|
data-slot | toolbar-group-button |
data-color | mirrors color |
data-icon-only | Present when icon is true |
ToolbarOverflowItem
A data-carrier wrapper for non-primitive toolbar controls (e.g. Input, custom elements) that cannot accept the menuItem / visibility props directly. Place it as a direct child of <Toolbar> when overflow is enabled.
| Prop | Type | Default |
|---|---|---|
children | React.ReactNode | — (required) — rendered in the toolbar when space allows |
menuItem | React.ReactNode | — — rendered inside the overflow dropdown when this item is hidden |
visibility | 'always-visible' | 'always-hidden' | undefined | undefined |
ToolbarOverflowItem does not render any DOM element of its own; it delegates all rendering to the parent Toolbar.
ToolbarAdaptiveItem
A data-carrier for variable-width toolbar controls with three responsive tiers: expanded, collapsed, and optional menuItem in the ellipsis menu. Place it as a direct child of <Toolbar> when overflow is enabled.
| Prop | Type | Default |
|---|---|---|
expanded | React.ReactNode | — (required) — rendered when the toolbar is wide enough |
collapsed | React.ReactNode | — (required) — compact inline fallback when the toolbar is narrower than collapseBelow |
collapseBelow | number | 480 — toolbar width in px (height when orientation="vertical") below which collapsed is shown |
menuItem | React.ReactNode | — — rendered inside the overflow dropdown when the entire slot is hidden |
visibility | 'always-visible' | 'always-hidden' | undefined | undefined |
ToolbarAdaptiveItem does not render any DOM element of its own when overflow is enabled; the parent Toolbar renders a [data-slot="toolbar-adaptive-item"] wrapper with [data-adaptive-state="expanded" | "collapsed"].
ToolbarZoneSplitter
A layout-only flex-grow spacer for separating toolbar zones. It absorbs remaining space along the toolbar axis so items before it stay left-aligned and items after it group toward the trailing edge. It does not participate in overflow collapse, keyboard focus, or the overflow menu.
| Prop | Type | Default |
|---|---|---|
className | string | — |
| Data attribute | Values |
|---|---|
data-slot | toolbar-zone-splitter |
Accessibility
Label the Toolbar and each ToolbarToggleGroup with aria-label (or aria-labelledby). Icon-only ToolbarToggleItem values need aria-label. Radix uses roving tabindex across toolbar items. See Radix Toolbar — accessibility.
Keyboard interactions
| Key | Description |
|---|---|
Tab | Moves focus into the toolbar (first item). |
Space | Activates or toggles the focused item. |
Enter | Activates or toggles the focused item. |
ArrowRight / ArrowDown | Moves focus to the next item (depends on orientation). |
ArrowLeft / ArrowUp | Moves focus to the previous item. |
Home | Moves focus to the first item. |
End | Moves focus to the last item. |
Source in the repo: packages/experience-system/src/components/Toolbar/Toolbar.tsx. Agent-oriented contracts: packages/experience-system/src/components/Toolbar/Toolbar.instructions.md.