ButtonGroup
PolymorphicJoin a row or column of Buttons and ActionIcons into one segmented control — inner corners flattened, adjacent borders collapsed to a single hairline.
ButtonGroup wraps a set of Button or ActionIcon siblings so they sit edge-to-edge as one unit: the inner corners are flattened and the adjacent borders collapse into a single hairline. Use it for view switchers, segmented toolbars, and split actions.
It is a pure layout wrapper — it deliberately doesn't touch its children's props. Give every child the same size (and usually the same variant / color) so their edges line up.
import { Button } from '@42/ui-react/button';
import { ButtonGroup } from '@42/ui-react/button-group';
<ButtonGroup>
<Button variant="outline" color="gray">Left</Button>
<Button variant="outline" color="gray">Center</Button>
<Button variant="outline" color="gray">Right</Button>
</ButtonGroup>Playground
Flip orientation and switch presets — the code panel updates as you go.
Component
Dir
Presets
Props
Code
<Component orientation="horizontal" />Basic
Only the outermost corners keep their radius; the inner ones square off. Adjacent borders overlap into one hairline instead of doubling up.
Code
<ButtonGroup> <Button variant="outline" color="gray">Left</Button> <Button variant="outline" color="gray">Center</Button> <Button variant="outline" color="gray">Right</Button></ButtonGroup>Orientation
orientation="vertical" joins the children into a column instead of a row — the top/bottom edges flatten rather than the left/right ones. The horizontal join uses logical properties, so it mirrors correctly under dir="rtl".
Code
<ButtonGroup orientation="vertical"> <Button variant="outline" color="gray">Top</Button> <Button variant="outline" color="gray">Middle</Button> <Button variant="outline" color="gray">Bottom</Button></ButtonGroup>Action icons
Group ActionIcons into a compact toolbar. Each still needs its own aria-label.
Code
<ButtonGroup> <ActionIcon variant="outline" aria-label="Bold"><Bold /></ActionIcon> <ActionIcon variant="outline" aria-label="Italic"><Italic /></ActionIcon> <ActionIcon variant="outline" aria-label="Underline"><Underline /></ActionIcon></ButtonGroup>Split action
Mix a Button with a trailing ActionIcon — the classic split-button pattern (a primary action plus a menu trigger).
Code
<ButtonGroup> <Button variant="filled" color="brand">Save</Button> <ActionIcon variant="filled" color="brand" aria-label="More save options"> <ChevronDown /> </ActionIcon></ButtonGroup>Variants
Every Button / ActionIcon variant works, and the group keeps adjacent children visually separated whichever one you use:
- Bordered variants (
outline,light,default) collapse their two shared borders into a single hairline. - Borderless variants (
filled,subtle) have no border to collapse, so the group adds an adaptive divider on the shared edge — a translucentcurrentColorline that reads as a soft light seam on afilledfill and a faint neutral one onsubtle. Because it follows the text color it stays correct on every palette color and in dark mode.
A hovered or focused child always lifts above its neighbours, so its full border and focus ring stay visible.
Mixed variants
Children don't have to share a variant — but keep in mind this is not recommended.
The seam adapts across a variant boundary: bordered neighbours collapse to a hairline, borderless ones show the adaptive divider.
Keep the size consistent so the heights still line up.
Code
<ButtonGroup> <Button variant="subtle">Day</Button> <Button variant="filled" color="brand">Week</Button> <Button variant="subtle">Month</Button></ButtonGroup><ButtonGroup> <Button variant="outline" color="gray">Cancel</Button> <Button variant="filled" color="brand">Confirm</Button></ButtonGroup>Equal heights
If a horizontal row ends up with different-height children — most often a mix of sizes — the group stretches them all to the tallest so the row stays aligned. Each button keeps its own width, and ActionIcon keeps its square. For a uniform look, still prefer a single size; this is the safety net when you can't.
Code
<ButtonGroup> <Button size="sm">Small</Button> <Button size="lg">Large</Button> <Button size="sm">Small</Button></ButtonGroup>Polymorphic with asChild
ButtonGroup renders a div with role="group". Use asChild to swap that host element — e.g. a <section aria-label="…"> — while keeping the joining styles.
<ButtonGroup asChild>
<section aria-label="Text formatting">
<ActionIcon variant="outline" aria-label="Bold"><Bold /></ActionIcon>
<ActionIcon variant="outline" aria-label="Italic"><Italic /></ActionIcon>
</section>
</ButtonGroup>API
Prop
Type
All other props are forwarded to the underlying div (or the asChild element).
Accessibility
- The root carries
role="group". When the grouping is meaningful to assistive tech (e.g. a toolbar or a set of related actions), label it witharia-label/aria-labelledby. - The group doesn't manage focus or selection — each child stays an independent, individually focusable control. For a single-choice segmented toggle with roving focus, use
SegmentGroupinstead. - Icon-only children must set their own
aria-label(seeActionIcon).
ActionIcon
Square, icon-only sibling of Button. Same (variant × color) model plus its own radius scale.
Field
Label, description, and error scaffold for any form control — built on Ark UI's Field, so the accessible name, description, and invalid state wire themselves to whatever control you nest inside.