ButtonGroup
Lay out related buttons with consistent spacing, or join them into one segmented control.
Installation
- Install the package.
npm install @hashintel/ds-components- Import the component.
import { Button, ButtonGroup } from "@hashintel/ds-components";
export const EntityActions = () => (
<ButtonGroup>
<Button tone="brand" iconName="plus">
Create entity
</Button>
<Button variant="subtle">Import</Button>
</ButtonGroup>
);When to use
- Do: Use ButtonGroup when two or more related actions sit together — page headers, dialog footers, toolbars — so gaps, alignment, and wrapping stay consistent.
- Do: Use
variant="segmented"to join tightly coupled commands into one control: pagers, zoom, playback. - Do: Use
alignedTo="right"for footer actions anchored to the trailing edge of a dialog or form.
When not to use
- Don't: Don't use a segmented group to pick one value from a visible set — use SegmentedControl, or RadioGroup in forms.
- Don't: Don't group unrelated controls just for layout — the group role announces them as related; use a plain flex container instead.
- Don't: Don't line up 4+ secondary actions — fold the overflow into an EllipsisMenu.
- Don't: Don't mix button sizes inside a segmented group — row rounding assumes equal heights; give every child the same size.
Examples
Basic
The default group is spaced with an md gap: buttons sit apart, keep their own styling, and wrap as a set.
Segmented
segmented joins the buttons into a single control with shared borders. Use it for tightly coupled commands that read as one unit. Adjacent solid buttons separate with a hairline gap instead of a shared border.
Spacing
Match spacing to the children's size — the scale is fitted to Button sizes with the same value. It has no effect on segmented groups, whose buttons sit flush.
Aligned to the right
A group fills its container and aligns buttons to the left edge by default. Use alignedTo="right" for dialog and form footers anchored to the trailing edge.
Reversed
reverse flips the buttons by reversing the DOM, so focus and tab order follow the visual order. Use it when a surface mirrors the usual priority order instead of reordering children by hand.
Keeping one row
Groups wrap when they run out of width. Set noWrap in fixed chrome — toolbars, table headers — where the group should keep one line and overflow instead.
Behavior
- Each direct child of a segmented group must render exactly one element and pass data attributes through to its control — every Button does, including tooltip-wrapped ones. Children that render fragments or swallow unknown props misalign the per-row rounding; a dev-only console warning catches count mismatches.
- Adjacent segmented buttons overlap their borders, and the hovered or focused button rises above its neighbors so its border and focus ring aren't clipped. If a segmented button group wraps, border radius is applied to each line.
spacingonly affects spaced groups; a segmented group's buttons sit flush, with a fixed small gap between wrapped rows.
Accessibility
What ButtonGroup provides
- Renders a
role="group"container, so assistive technology announces the buttons as a related set. reversereorders the DOM, not just the presentation, so tab order always matches what's on screen.
What you must do
- Don't rely on segmented styling to convey a selected value — selection needs SegmentedControl's radio semantics.
API reference
| Prop | Type | Description |
|---|---|---|
| variant | "spaced" | "segmented" default: "spaced" | spaced spaces the buttons apart; segmented joins them into a single control with shared, overlapping borders. |
| spacing | "xxs" | "xs" | "sm" | "md" | "lg" default: "md" | The gap between buttons in a spaced group; no effect on segmented groups, whose buttons share a border. Each value is tuned to the matching Button size, so it usually equals the children's size. |
| reverse | boolean default: false | Whether to reverse the visual and tab order of the buttons. |
| alignedTo | "left" | "right" default: "left" | Which edge of the available width the buttons align to. |
| noWrap | boolean default: false | Whether to keep the buttons on one line, overflowing instead of wrapping. |
Was this page helpful?