Button Group
A container that welds related buttons, inputs, selects and menus into one connected control with shared seams.
- Status
- Since
0.10.0- Accessibility pattern
- labelled group role
Last updated
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Button Group before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify button-groupInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/button-groupRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/separator.
Usage
import {
ButtonGroup,
ButtonGroupSeparator,
ButtonGroupText,
} from "@/components/ui/button-group";
<ButtonGroup>
<Button>Button 1</Button>
<Button>Button 2</Button>
</ButtonGroup>;Anatomy
Examples
Composition
Use the following composition to build a ButtonGroup:
ButtonGroup
├── Button or Input
├── ButtonGroupSeparator
└── ButtonGroupTextButtonGroup vs ToggleGroup
- Use the
ButtonGroupcomponent when you want to group buttons that perform an action. - Use the
ToggleGroupcomponent when you want to group buttons that toggle a state.
Orientation
Set the orientation prop to change the button group layout. The horizontal group flattens the
inline-end corners and drops the inline-start border of every button after the first; the vertical
group does the same along the block axis.
Size
Control the size of buttons using the size prop on individual buttons — the group owns the seams,
never the height.
Nested
Nest ButtonGroup components to create button groups with spacing: a group whose children are
groups picks up gap-2, so the seams stay welded inside each inner group and open up between them.
Separator
The ButtonGroupSeparator component visually divides buttons within a group.
Buttons with variant outline do not need a separator since they have a border. For other variants,
a separator is recommended to improve the visual hierarchy.
Split
Create a split button group by adding two buttons separated by a ButtonGroupSeparator.
Input
Wrap an Input component with buttons. The group stretches the input with flex-1, so the button
keeps its intrinsic width.
Input Group
A button group welds to an InputGroup exactly as upstream's does:
the group owns the shared seams, InputGroup owns the bordered field surface and its addons.
Dropdown Menu
Create a split button group with a DropdownMenu component. Render the trigger through a Button
so the menu inherits the group's seam.
Select
Pair with a Select component. The group gives the trailing trigger back its inline-end radius, so
a select at the end of a row does not read as clipped.
Popover
Use with a Popover component — the same split-action shape as the dropdown, with a form inside
the surface instead of a menu.
RTL
Every seam is written in logical properties (rounded-e-*, rounded-s-*, border-s-0), so a
right-to-left document mirrors the welded corners and the dropped borders with no extra work.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | "horizontal" | "vertical" | null | — |
ButtonGroupSeparator adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
ButtonGroupText adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
Accessibility
- The
ButtonGroupcomponent has theroleattribute set togroup. - Use Tab to navigate between the buttons in the group. The group is not a roving-focus widget: every member is its own tab stop.
- Use
aria-labeloraria-labelledbyto label the button group. - The group raises a focused member with
*:focus-visible:relativeand*:focus-visible:z-10, so the global outline frombase.cssis never clipped by the neighbour that overlaps its seam. ButtonGroupSeparatorrenders a Base UISeparator, which isrole="separator"with the rightaria-orientation— it is announced as a divider, not as another control.- An icon-only button inside a group still has no visible text; give it an
aria-label.
| Contract | States tested |
|---|---|
| Behaviour | default |
| Accessibility | native-or-base-ui-semantics, browser-accessibility-test |
| Visual | default |
Do / Don't
Deviations
Upstream's file plus packages/ui/upstream/patches/button-group.patch. Every hunk:
- DOC-2 —
cnis imported from@vegastack/design. - API-16 —
"use client"is added:useRendercallsReact.useRefinternally, so the module throws on import in a React Server Component without the boundary.