Button group
Buttons can be grouped by wrapping them in buttonGroup(). They need to be immediate children: the group rounds the first and last button and pulls the rest together, so anything between them breaks the seam.
Basic button group
buttonGroup({ label: 'Basic button group' },
button('One'),
button('Two'),
button('Three'),
)Variants
The group itself carries no colour. Set variant and color on the buttons, and keep them the same across the group — that is what makes it read as one control.
stack({ gap: 'md', align: 'flex-start' },
buttonGroup({ label: 'Solid' },
button({ variant: 'solid' }, 'One'),
button({ variant: 'solid' }, 'Two'),
button({ variant: 'solid' }, 'Three'),
),
buttonGroup({ label: 'Outline' },
button({ variant: 'outline', color: 'neutral' }, 'One'),
button({ variant: 'outline', color: 'neutral' }, 'Two'),
button({ variant: 'outline', color: 'neutral' }, 'Three'),
),
buttonGroup({ label: 'Soft' },
button({ variant: 'soft' }, 'One'),
button({ variant: 'soft' }, 'Two'),
button({ variant: 'soft' }, 'Three'),
),
)Sizes and colors
stack({ gap: 'md', align: 'flex-start' },
buttonGroup({ label: 'Small' },
button({ size: 'sm', variant: 'outline', color: 'neutral' }, 'Left'),
button({ size: 'sm', variant: 'outline', color: 'neutral' }, 'Center'),
button({ size: 'sm', variant: 'outline', color: 'neutral' }, 'Right'),
),
buttonGroup({ label: 'Large' },
button({ size: 'lg', variant: 'soft', color: 'danger' }, 'Cancel'),
button({ size: 'lg', variant: 'soft', color: 'danger' }, 'Discard'),
),
)Links
Buttons with an href group exactly the same way — for a row of things that each go somewhere, none of which is the one you are on.
buttonGroup({ label: 'Share' },
button({ href: '#rss', variant: 'outline', color: 'neutral' }, 'RSS'),
button({ href: '#json', variant: 'outline', color: 'neutral' }, 'JSON'),
button({ href: '#sitemap', variant: 'outline', color: 'neutral' }, 'Sitemap'),
)Button group or toggle group?
A button group is a container: it joins whatever you put in it and holds no state. If one of the items is selected — a segmented control, a filter, the section you are currently in — that is toggle group, which builds the items from data and marks the active one for you.
The rule of thumb: if pressing one makes the others wrong, it is a toggle group. If each does its own separate thing, it is a button group.
stack({ gap: 'lg' },
stack({ gap: 'xs' },
text({ variant: 'caption', tone: 'muted' }, 'buttonGroup — three separate actions'),
buttonGroup({ label: 'Row actions' },
button({ variant: 'outline', color: 'neutral' }, 'Edit'),
button({ variant: 'outline', color: 'neutral' }, 'Duplicate'),
button({ variant: 'outline', color: 'neutral' }, 'Delete'),
),
),
stack({ gap: 'xs' },
text({ variant: 'caption', tone: 'muted' }, 'toggleGroup — one choice out of three'),
toggleGroup({
label: 'Text alignment',
value: 'Center',
items: ['Left', 'Center', 'Right'],
}),
),
)With an icon button
buttonGroup({ label: 'Editor actions' },
button({ variant: 'outline', color: 'neutral' }, 'Save'),
iconButton({
label: 'More actions',
variant: 'outline',
color: 'neutral',
icon: icon('more-horizontal'),
}),
)Props
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Accessible name for the group; becomes aria-label on role="group". |
Everything else falls through to the wrapper. The buttons inside take their own props — see button().