Toggle group
A row of toggles for one choice or many, with a fill that slides to the picked item, joined or spaced items and arrow-key focus.
Used in Pro blocks: Diff Review, Markdown, Test Results, and 5 more
pnpm dlx shadcn@latest add https://hextaui.com/r/toggle-group.jsonAdds the component, the HextaUI theme tokens and any HextaUI components it depends on.
Add the theme tokens to your global CSS, if you haven’t yet.
Install the dependencies.
pnpm add @base-ui/react class-variance-authority cnCopy and paste the following code into your project.
components/ui/toggle-group.tsx components/ui/toggle.tsx lib/motion.ts Update the import paths to match your project setup.
When one item can be on at a time, a single fill slides to the picked item and takes its shape, so the group reads as one control. With multiple, each item fills on its own. The fill is placed before the first paint, so it never slides in on load.
variant="outline" joins the items into one bordered control.
multiple lets any number of items be on, like text formatting.
size on the group sets every item: sm, default or lg.
spacing puts a gap between items on the spacing scale. Outline items with a gap keep their own borders and fills.
Put an icon before the label. Icon-only items need an aria-label.
orientation="vertical" stacks the items and moves focus with ↑ and ↓.
Disable one item, or the whole group with disabled. Disabled items are skipped by the keyboard.
Pass value and onValueChange. Ignoring the empty value keeps one item on, so pressing the picked item again does nothing.
Item order, joined corners, the sliding fill and arrow keys follow the reading direction.
| Key | Action |
|---|---|
| Tab | Moves into the group on the last focused item, then out of it. |
| ←→ | Moves between items, wrapping at the ends. ↑ and ↓ when vertical. |
| HomeEnd | Moves to the first or last item. |
| EnterSpace | Turns the focused item on or off. |
- The group has the
grouprole; give it anaria-label. Each item is a button witharia-pressed. - Only one item is in the tab order, so the group is a single Tab stop and arrow keys move inside it.
- Without
multiple, pressing the picked item turns it off. Control the value to keep one item on. - With reduced motion, the fill fades instead of sliding.
| Prop | Type | Default |
|---|---|---|
value | string[] | – |
defaultValue | string[] | – |
onValueChange | (value: string[], details) => void | – |
multiple | boolean | false |
variantSets every item's variant. | "default" | "outline" | "default" |
sizeSets every item's size. | "default" | "sm" | "lg" | "default" |
spacingGap between items on the spacing scale. 0 joins outline items. | 0 | 0.5 | 1 | 1.5 | 2 | 2.5 | 3 | 4 | 5 | 6 | 8 | 0 |
orientation | "horizontal" | "vertical" | "horizontal" |
loopFocus | boolean | true |
disabled | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="toggle-group" | The root. |
data-slot="toggle-group-indicator" | The sliding fill, rendered without multiple. data-visible when an item is on. |
data-variant / data-size / data-spacing | The group's variant, size and spacing. |
data-orientation | "horizontal" or "vertical". |
data-multiple | Present when many items can be on. |
data-disabled | Present when the group is disabled. |
| Prop | Type | Default |
|---|---|---|
valueIdentifies the item in the group's value. | string | – |
disabled | boolean | false |
variantUsed when the group sets none. | "default" | "outline" | – |
sizeUsed when the group sets none. | "default" | "sm" | "lg" | – |
nativeButton | boolean | true |
render | ReactElement | (props, state) => ReactElement | <button> |
| Attribute | Description |
|---|---|
data-slot="toggle-group-item" | Each item. |
data-pressed | The item is on. |
data-disabled | The item is disabled. |
data-variant / data-size | The item's resolved variant and size. |
- MotionThe easing curves, durations and reduced-motion check every component animates with, plus hooks for size morphs and sliding highlights.
- ToggleA button that stays on or off, with a fill that settles in when pressed, a clear hover-to-on step and icons that can fill with the state.
- ButtonButtons in every variant and size, with a built-in loading, success and error flow that skips the spinner for fast requests.
- Button groupButtons joined into one control, with shared seams, separators, text addons, nesting and vertical stacks.
Used in Pro blocks
Blocks that build on Toggle group.
- Diff ReviewA review of an agent’s file edits before they land. Handles accept or reject per change, comments back to the agent and edits still streaming.
- MarkdownMarkdown rendering for AI answers. Handles streaming without half-typed syntax, tables you can copy and inline citations.
- Test ResultsAn agent’s test run, shown as it happens. Tests stream in and fold into a summary, with every failure left open and its diff in view.
- Voice ModeVoice conversations with an AI assistant. Handles listening, interruptions and captions, in a full-screen session or an in-chat voice pill.
- API keysThe API keys page of an AI product. Handles a secret shown only once, revoke with undo and rotation with a grace period.
- ModelsThe Models page of an AI product’s settings. Handles the default model and effort, providers and custom servers with a connection test.
Every block comes with HextaUI Pro, and Prompt Input is free.