Button group
Buttons joined into one control, with shared seams, separators, text addons, nesting and vertical stacks.
pnpm dlx shadcn@latest add https://hextaui.com/r/button-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/button-group.tsx components/ui/separator.tsx Update the import paths to match your project setup.
Button group joins the buttons you already use. Install <Button /> as well.
Set size on each button. Joined corners stay square at every size, and the outer corners keep the button’s radius.
Filled buttons sit flush, so add a <ButtonGroupSeparator /> between them. A dropdown trigger can be any segment.
Separators between icon buttons, with a toggle that reports its pressed state.
When a button’s label changes width during loading, success or error, the segments after it slide along instead of jumping.
orientation="vertical" stacks the segments. Separators turn horizontal automatically.
Groups inside a group become separate clusters with a gap between them, each joined on its own.
<ButtonGroupText /> adds a label or prefix. Render it as a <label> to name the input. The input stretches to fill the row.
Disable or mark a single segment with aria-invalid. An invalid segment rises above its neighbours so its red ring is never covered.
Give the group a width and each button flex-1 to split the space evenly.
Use render to output a <fieldset> inside forms.
Joined corners, separators and nested clusters mirror in right-to-left layouts.
| Key | Action |
|---|---|
| Tab | Moves through the segments in order. The focused segment rises so its ring and border are never covered. |
| EnterSpace | Activates the focused button. |
- The group has
role="group". Give it anaria-labelthat describes the set, like “Message actions”. - Icon-only segments need their own
aria-label. - Separators are exposed as separators with the right orientation. Add
decorativewhen the line is only visual.
Every part renders a <div> and accepts its attributes.
| Prop | Type | Default |
|---|---|---|
orientation | "horizontal" | "vertical" | "horizontal" |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="button-group" | Target groups in CSS. |
data-orientation | The current orientation. |
| Prop | Type | Default |
|---|---|---|
orientationDefaults to the opposite of the group’s orientation. | "horizontal" | "vertical" | – |
decorativeHide the line from screen readers. | boolean | false |
render | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="button-group-separator" | Target separators in CSS. |
data-orientation | The resolved orientation. |
| Prop | Type | Default |
|---|---|---|
renderRender a <label> to name an input in the group. | ReactElement | (props, state) => ReactElement | <div> |
| Attribute | Description |
|---|---|
data-slot="button-group-text" | Target text segments in CSS. |
- SeparatorA hairline that divides content horizontally or vertically, with an optional label and a decorative mode for purely visual lines.
- ButtonButtons in every variant and size, with a built-in loading, success and error flow that skips the spinner for fast requests.
- 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.
- Toggle groupA 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.