ButtonGroup
MD3 Expressive button group: an invisible container that spaces Button /
IconButton children and reshapes them on press. It can also own a
single- or multi-select model — a connected single-select group is the
Expressive replacement for the segmented button.
Standard
standard (the default) spaces the buttons by the size's between-space
(18 / 12 / 8 / 8 / 8dp for xs–xl). Pressing a button widens it by 15%
and squeezes its neighbours — press and hold to see it.
<ButtonGroup aria-label="Actions">
<IconButton variant="tonal" icon={<EditIcon />} aria-label="Edit" />
<Button variant="filled">Share</Button>
<IconButton variant="tonal" icon={<SearchIcon />} aria-label="Search" />
</ButtonGroup>
Connected
connected joins the buttons with a 2dp gap: the outer corners stay fully
round, the inner corners use a per-size radius that tightens while pressed.
Sizes
Match size on the group to the size of its buttons — it sets the
between-space (standard) or the inner corners and 48dp minimum width
(connected).
<ButtonGroup variant="connected" size="md">
<Button size="md">One</Button>
…
</ButtonGroup>
Single select
Set selectionMode="single" and give each child a value; the group owns
the state through value / defaultValue / onChange(event, value).
selectionRequired keeps one item selected (re-activating it does nothing).
const [view, setView] = useState<string | null>('week')
<ButtonGroup
variant="connected"
selectionMode="single"
selectionRequired
value={view}
onChange={(event, next) => setView(next)}
aria-label="Calendar view"
>
<Button variant="tonal" value="day">Day</Button>
<Button variant="tonal" value="week">Week</Button>
<Button variant="tonal" value="month">Month</Button>
</ButtonGroup>
Multi select
selectionMode="multiple" turns the children into toggle buttons; the value
is a string[].
<ButtonGroup
variant="connected"
selectionMode="multiple"
defaultValue={['bold', 'underline']}
onChange={(event, values) => setStyles(values)}
aria-label="Text style"
>
<IconButton value="bold" icon={<BoldIcon />} aria-label="Bold" />
…
</ButtonGroup>
Vertical
Accessibility
- Arrow keys move focus between the buttons: ← / → when horizontal (mirrored in RTL), ↑ / ↓ when vertical. Home / End jump to the ends; disabled buttons are skipped.
selectionMode="single"renders aradiogroupofradioitems with a single Tab stop — arrow keys move focus and selection.selectionMode="multiple"renders toggle buttons witharia-pressed.- Name the group with
aria-label/aria-labelledby, and give icon-only children their ownaria-label.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | `Button` / `IconButton` children (give them a `value` when the group has a `selectionMode`). |
defaultValue | string | number | readonly string[] | string[] | null | null
[] | Unused without a selection model (kept for HTMLAttributes compatibility). Uncontrolled initial selected item value. Uncontrolled initial selected item values. |
onChange | ChangeEventHandler<HTMLDivElement, Element> | ((event: SyntheticEvent<Element, Event>, value: string | null) => void) | ((event: SyntheticEvent<...>, value: string[]) => void) | — | Native `change` events bubbling from descendants. Fires with the triggering event and the next selected value. Fires with the triggering event and the next selected values. |
orientation | enum | 'horizontal' | Layout direction. |
selectionMode | enum | 'none' | |
selectionRequired | boolean | false
false | Keep one item selected: re-activating the selected item does nothing. Keep at least one item selected. |
size | enum | 'sm' | Match the child button size: sets the between-space (standard) or the inner corners and 48dp minimum width (connected). |
value | string | string[] | null | — | Controlled selected item value (`null` = none). Controlled selected item values. |
variant | enum | 'standard' | `standard` spaces buttons and widens the pressed one; `connected` joins them into a single shape with small inner corners. |