Button Group
The ButtonGroup component allows you to group multiple buttons together, either horizontally or vertically. It is useful for organizing related actions.
The button data is passed via the buttonsProps prop, which should contain an array of props for each individual button.
For a group of icon-only buttons, see the icon-only Button Group.
Installation
import { ButtonGroup } from '@harnessio/ui/components'
return ( <ButtonGroup buttonsProps={[ { children: 'Button', onClick: () => { console.log('Button 1') } }, { children: 'Button', onClick: () => { console.log('Button 2') } }, { children: 'Button', onClick: () => { console.log('Button 3') } } ]} />)Layout Orientation
The ButtonGroup component accepts an orientation prop, which defines the layout direction: either horizontal or vertical.
Sizes
The ButtonGroup component accepts a size prop to control its overall dimensions. Supported values are: default, sm, and xs.
With Dropdown
Within buttonsProps, you can provide a dropdownProps property to define the dropdown content for a specific button.
Anatomy
A button group is composed of:
- Container — a wrapper that lays the buttons out as a single connected unit, either
horizontal(default) orvertical. - Buttons — two or more buttons built from
buttonsProps. They default to theoutlinevariant, and the first and last buttons get the group’s rounded outer corners. - Dropdown affordance (optional) — any button given
dropdownPropsopens a menu instead of firing a direct action.
The size prop scales every button in the group together.
Best practices
Do
- Group actions that are related and belong to the same task — a set of parallel choices or closely-linked commands.
- Keep the group small; a handful of buttons stays scannable, a long row does not.
- Switch to
orientation="vertical"in narrow columns or menus rather than letting a horizontal group overflow. - Keep the buttons visually consistent — the group is designed to read as one unit, so avoid mixing variants or themes across it.
Don’t
- Don’t collect unrelated actions into one group just to save space; it implies a relationship that isn’t there.
- Don’t use a button group as a set of mutually-exclusive options — that’s a job for a segmented control or radio group, which carry selection semantics.
- Don’t overload a single group with both direct actions and multiple dropdowns; split them up if the intent gets muddy.
Content
- Use sentence case for every button label — capitalize only the first word, along with any proper nouns or product names.
- Keep labels short and start with a verb: “Merge”, “Copy link”, “Run”.
- Keep labels parallel in structure so the grouped actions read as a set.
Accessibility
Keyboard
- Each button in the group is a native
<button>, so every one is an individual stop in the tab order and activates with Enter and Space. - Buttons configured with
dropdownPropsopen a Radix dropdown menu, which brings its own keyboard support — open with Enter / Space, move through items with the arrow keys, and close with Esc.
Labeling
- A text label is each button’s accessible name. For icon-only groups (
iconOnly), each button requirestooltipProps, which supplies the accessible name — this is enforced at the type level by the underlying Button. - Buttons with a dropdown expose their expanded/collapsed state through the Radix menu trigger.
Focus & states
- Each button shows the standard keyboard focus ring (
:focus-visible) using the--cn-focustoken, so the focused button in the group is always clear.
Known limitations
- The group container is a plain styled
<div>— it does not setrole="group"or a group label, so assistive tech announces the buttons individually rather than as a named set. If the grouping itself carries meaning, wrap it with your ownrole="group"andaria-label. - There is no roving focus or arrow-key navigation between the grouped buttons; they are separate tab stops. If you need single-tab-stop behavior with arrow-key movement (toolbar semantics), that has to be added by the consumer.
Properties
Type ButtonGroupButtonProps
type ButtonGroupTooltipProps = Pick< TooltipProps, "title" | "content" | "side" | "align">;
type BaseButtonProps = Omit< ButtonProps, "variant" | "size" | "theme" | "asChild" | "iconOnly" | "rounded" | "type">;
type DropdownPropsType = { content: ReactNode; contentProps?: typeof DropdownMenu.Content;};
type ButtonWithTooltip = BaseButtonProps & { tooltipProps: ButtonGroupTooltipProps; dropdownProps?: undefined;};
type ButtonWithDropdown = BaseButtonProps & { dropdownProps: DropdownPropsType; tooltipProps?: undefined;};
type ButtonGroupButtonProps = ButtonWithTooltip | ButtonWithDropdown;Prop | Required | Default | Type |
|---|---|---|---|
| orientation | false | horizontal | 'horizontal' | 'vertical' |
| className | false | string | |
| size | false | md | 'md' | 'sm' | 'xs' |
| buttonsProps | true | ButtonGroupButtonProps[] | |
| iconOnly | false | boolean |