Components
Button Group
Joins a set of related buttons into a single, visually-connected control — flattening the seams between adjacent members and collapsing their shared borders.
Basic usage
Wrap any number of Button elements — the group fuses their adjacent corners and borders into one segmented control. The wrapper is w-fit, so it never stretches unless you give it a width.
Orientation
Set orientation="vertical" to stack members in a column — ideal for a floating toolbar. The join flips with it: vertical members collapse their top borders instead of their left ones.
With a separator
Drop a ButtonGroupSeparator between members to split a primary action from a dropdown trigger. Unlike a standalone Separator it defaults to vertical and stretches to the full height of the group.
With static text
ButtonGroupText renders a non-interactive label that sits flush inside the group — perfect for steppers or a unit suffix. Icons inside it are auto-sized to 16px.
Joining an input
The group is not limited to buttons. A direct <input> child is given flex-1 so it absorbs the leftover width, and a SelectTrigger child keeps its natural width instead of stretching. Give the group a width and the input follows.
Nested groups
A group whose children are themselves groups switches from fusing to spacing: the clusters are set 8px apart while each cluster stays welded internally. This is how you build a toolbar with related sets of controls without one twelve-button bar.
Props
ButtonGroup
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | 'horizontal' | 'vertical' | 'horizontal' | Lays the children out in a row or a column, joining their adjacent edges accordingly. Whatever you pass is mirrored onto the wrapper as data-orientation; leave it off and the attribute is absent, so do not select on data-orientation="horizontal" to catch the default. |
className | string | — | Additional Tailwind classes to merge onto the group wrapper. |
...rest | React.ComponentProps<'div'> | — | All native div attributes are forwarded to the wrapper, which always carries role="group" and data-slot="button-group". |
ButtonGroupSeparator
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | 'horizontal' | 'vertical' | 'vertical' | Direction of the dividing line drawn between two members of the group. Note this is the opposite default from a standalone Separator. |
decorative | boolean | true | Inherited from Separator — when true the line is hidden from the accessibility tree. |
className | string | — | Additional Tailwind classes to merge. |
...rest | React.ComponentProps<typeof Separator> | — | All Radix Separator props are forwarded. |
ButtonGroupText
| Prop | Type | Default | Description |
|---|---|---|---|
asChild | boolean | false | Merges props onto the child element instead of rendering a div, via Radix Slot. |
className | string | — | Additional Tailwind classes to merge. |
...rest | React.ComponentProps<'div'> | — | All native div attributes are forwarded. |
The layout recipe is exported as buttonGroupVariants if you need the joining rules on a wrapper you render yourself.
Accessibility
- The wrapper carries
role="group", signalling to assistive technology that the buttons form a single related set. - Each member remains an independent native
<button>, so Tab, Enter, and Space behave exactly as on a standalone button. - The focused member is lifted above its neighbours — the group sets
focus-visible:relativeandfocus-visible:z-10on every child — so the brand focus outline is never clipped by an adjacent collapsed border. - Always pass an
aria-labelto icon-only members, since the joined layout offers no visible text label. - The separator is decorative by default and is correctly omitted from the accessibility tree.
- Give the group its own
aria-labelwhen the set needs a collective name (“Text alignment”), and label a joined input separately — the group role does not name its members.