ButtonGroup

Atom
Segmented controls, split buttons, attached groups.

Playground

Every control sets a real prop of ButtonGroupProps. The JSX below updates as you go.
Props
Kind
pick one
Size
pick one
active
number
1
iconsOnly
boolean
block
boolean
disabled
boolean
JSX · ButtonGroupProps
<ButtonGroup segmented active={1} />

Variants

9 examples from src/atoms/button-group/button-group.md, each rendered live from its own JSX.
Small
<ButtonGroup items={["Day", "Week", "Month"]} small />
Large
<ButtonGroup items={["Day", "Week", "Month"]} large />
Disabled
<ButtonGroup items={["Day", "Week", "Month"]} disabled />
Block
<ButtonGroup items={["Day", "Week", "Month"]} block />
Block spaced
<ButtonGroup spaced items={["Edit", "Duplicate", "Archive"]} block />
Measure
<Container>
<Column>
<ButtonGroup items={["Day", "Week", "Month"]} xs start />
<ButtonGroup spaced items={["Edit", "Duplicate", "Archive"]} md />
</Column>
</Container>
Yesterday
Stepper
<ButtonGroup stepper items={["Yesterday", "Today", "Tomorrow"]} />
Split
In glass mode the split menu is a dense-layer glass card under the group, whose shared glass, primary label, divider and chevron stay in place. The primary action and the chevron stay two fixed targets throughout, and a group disabled while its menu is open closes it. Solid mode paints the skin's own menu.
<ButtonGroup split items={["Save"]} menu={["Save as draft", "Save and close", "Save a copy"]} />
Icon segments
An item may pair its label with a kit glyph ({ label, icon }); iconsOnly renders each segment as the glyph alone, with the label as the segment's accessible name. An icon-only segmented control: a view switcher, a form-factor switcher.
<ButtonGroup
iconsOnly
items={[
{ label: "Phone width", icon: "smartphone" },
{ label: "Tablet width", icon: "tablet" },
{ label: "Desktop width", icon: "monitor" }
]}
/>

Sizes

small
default
large

States

Hover, focus and press are pinned so they can be compared side by side; the rest are real props.
default
focus
disabled

Anatomy

1
Track
2
Segment
3
Active thumb
Dimmed parts appear only in some states or variants.

Props

23 props on ButtonGroupProps, grouped as they are declared in source. Required props are marked *.
Prop
Type
Default
Description
Props
4 props
items
ButtonGroupItem[]
–
Segments for segmented/spaced: a label string, or { label, icon } for a leading kit glyph. The stepper cycles the labels (icons ignored there).
active
number
–
Selected segment index (segmented, CONTROLLED), or the stepper's initial index. Omit for uncontrolled use.
defaultActive
number
–
Initial selected segment index for uncontrolled use (a bare segmented control selects on press).
onSelect
(index: number, item: string, event: GestureResponderEvent) => void
–
Called with the pressed/selected index and item (and, for the stepper, the new index).
Kind
pick one
8 props
segmented
boolean
false
split
boolean
false
stepper
boolean
false
spaced
boolean
false
attached
boolean
false
Buttons joined in one hairline box on the raised fill, divided by full-height rules, the selected one on the raised step (an alignment or a view picker drawn as buttons rather than a track and thumb). It selects as segmented does and takes active, defaultActive and block.
decrementIcon
ReactNode
–
The stepper's decrement end, in place of its built-in glyph (a zoomOut Icon for a zoom control). The stepper kind only.
incrementIcon
ReactNode
–
The stepper's increment end, in place of its built-in glyph (a zoomIn Icon).
menu
string[]
–
Related actions shown in the split kind's chevron dropdown.
Size
pick one
11 props
small
boolean
false
large
boolean
false
compact
boolean
false
Density of the segmented row: the segments keep their size's height and take the small label on a 12 px inset (the hand-off component page's platform switch, a default-height track of 12 px labels). On the web only: a platform that ships the control keeps its one density. The split, stepper and spaced kinds ignore it.
rounded
boolean
false
A capsule segmented track with equal cells and a sliding selection thumb.
iconsOnly
boolean
false
Segmented/spaced only: each segment renders its icon alone and the item's label becomes the segment's ACCESSIBLE name (an icon-only view or form-factor switcher). An item without an icon keeps its visible label, with a dev-only warning. Split and stepper ignore it (dev-only warning).
steadyPress
boolean
false
Keep segmented and spaced cells at their resting opacity during a press.
accessibilityLabel
string
–
Accessible name for the segmented row (the tablist), announcing what the group switches (e.g. "Preview form factor"). Segmented kind only: spaced peers are independent buttons with no group role.
block
boolean
false
Stretch the group to the container width, the segments sharing the space equally. An orthogonal layout modifier for the segmented and spaced kinds; split and stepper ignore it (their cells are fixed-width chrome) with a dev-only warning. A measure step (sm, lg, …) does the same up to that width of the scale.
disabled
boolean
false
testID
string
–
E2E hook forwarded to the root element.
style
LayoutStyle
–
Composition within a parent only, never a restyle hook and never a width: the parent layout container provides the bounds.
MeasureProps · width steps xxxs 192 · xxs 256 · xs 320 · sm 384 · md 448 · lg 512 · xl 576 · xxl 672 · xxxl 768 · wide 896 · wider 1024 · widest 1152 · page 1280, plus start to pin to the leading edge.

Accessibility and keyboard

Keyboard
Tab
Enters the group on the checked option
ArrowDown
ArrowRight
Checks the next option
ArrowUp
ArrowLeft
Checks the previous option
Space
Checks the focused option
Semantics
Roles
button
menu
menuitem
tab
tablist
States
disabled
expanded
selected
ARIA on web
aria-disabled
aria-expanded
aria-hidden
aria-label
aria-selected
Below the platform minimum, the touch area grows to 44 pt on iOS and 48 dp on Android without moving the layout.

Platform skins

The playground's props on each platform: same tree, native shapes, type and touch sizes.
Web
control 10 · field 10 · card 16 · Instrument Sans
iOS
capsule controls · field 8 · card 12 · SF Pro · 44 pt
Android
Material 3 stadium · field 4 · card 12 · Roboto · 48 dp

Usage

<ButtonGroup items={["Day", "Week", "Month"]} />
On the web the segmented control is the v4 hand-off's: the options sit in a rounded track on the raised fill, and the selected one rides a flat card thumb with a small shadow, the rest in the muted ink. It is 30, 36 or 44 px tall at the small, default and large sizes. The split button is the green call-to-action pill with its chevron half, and the stepper and the spaced peers are hairline pills the height of a Button beside them.
Pass rounded for a capsule segmented track with equal cells and one sliding selection thumb. The small capsule is 32 px tall, as in the docs appearance picker. Its spring transition follows Reduce Motion. On iOS and Android the groups keep their platform shapes (the iOS segmented control, the Material 3 segmented button) in the theme's colours.
All four kinds follow the theme's surface mode. Toggle Liquid Glass in the docs, or use <ThemeProvider glass> in an app: segmented groups paint a glass selection puck, in the segment's own corners, on the selected segment, a split group is its call to action's brand-tinted glass (the action colour under the material, as a primary Button), a stepper shares a glass capsule, and spaced peers get individual glass surfaces. Every kind keeps its solid size under glass: the material covers the track or cell's whole footprint and a hairline border gives way to the material's edge, so switching the surface mode never moves the page. Solid mode keeps each platform's existing skin. The material uses native Liquid Glass on supported iOS versions, the native blur or tint fallback on Android and older iOS, and the hand-off's frost in the browser. Reduce Transparency and Increase Contrast use the shared material's opaque fallbacks.

Tokens

Read from the playground render. Values follow the scheme and the ion hue.
--fd
--ft
--ln
--sf
--sf3
--sh-sm
--tx
--tx2

Related components