Skip to content
shadcn.io is not affiliated with official shadcn/ui

Shadcn Button Group for React and Tailwind

Visually connected buttons and related controls.

Installation

bunx --bun shadcn@latest add https://kit.dev/r/button-group.json

Anatomy

ButtonGroup
├── Button | Input | nested ButtonGroup
├── ButtonGroupText
└── ButtonGroupSeparator

Children share edges: first and last keep the outer radius, inner corners are squared, and adjoining start borders are removed.

Usage

import { Button } from "@/components/ui/button";
import {
  ButtonGroup,
  ButtonGroupSeparator,
} from "@/components/ui/button-group";
<ButtonGroup aria-label="Message actions">
  <Button variant="outline">Archive</Button>
  <Button variant="outline">Report</Button>
</ButtonGroup>

Label the group with aria-label or aria-labelledby. The root is a fieldset.

Orientation

orientation is horizontal by default. Vertical stacks children and joins top/bottom edges.

Horizontal

Vertical

Examples

Nested

Nest groups to cluster related actions. Nested groups get a gap-2 between them.

Separator

Use ButtonGroupSeparator when buttons have no adjoining border (for example variant="secondary"). Outline buttons already share an edge, so they usually do not need a separator.

With text

ButtonGroupText is a non-interactive label inside the cluster (prefixes, units, captions).

With input

With menu

With popover

Guides

Grouping semantics

ButtonGroup is a fieldset. Name it (aria-label or aria-labelledby). Icon-only children still need aria-label on the Button.

Nested groups

An outer group that contains other ButtonGroups adds gap-2 and does not collapse radii across the inner clusters. Put aria-label on the outer group and on each inner cluster when the actions differ.

Separators

ButtonGroupSeparator defaults to orientation="vertical" so it splits a horizontal row. In a vertical group, pass orientation="horizontal".

Button Group vs Toggle Group vs Button

Button GroupToggle GroupButton
RoleVisual cluster of independent actionsOne or more pressed statesA single action
StateNone of its ownvalue / onValueChangedisabled / isLoading

RTL

Horizontal joining uses logical radii (rounded-s-none, rounded-e-none) and border-s-0, so the cluster follows text direction. Vertical joining uses top/bottom edges.

API Reference

shadcn.io Button Group is a styled fieldset (Ark factory), not a Zag machine. There is no context hook or root provider.

asChild merges props onto a single child element.

ButtonGroup

Root. Renders a fieldset.

PropTypeDefaultDescription
orientation"horizontal" | "vertical""horizontal"Join along the inline axis or the block axis.
asChildbooleanfalseRender the child element instead of a fieldset.
classNamestring-Class names on the root.
aria-labelstring-Accessible name of the group. Prefer this or aria-labelledby.

Native fieldset attributes (disabled, form, name, …) pass through. disabled on a fieldset disables descendant controls.

AttributeDescription
data-slotbutton-group
data-orientation"horizontal" or "vertical"

ButtonGroupText

Non-interactive label inside the cluster. Renders a div.

PropTypeDefaultDescription
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the text.
AttributeDescription
data-slotbutton-group-text

ButtonGroupSeparator

Visual divider. Renders Separator (role="separator").

PropTypeDefaultDescription
orientation"horizontal" | "vertical""vertical"vertical is a thin inline divider for a horizontal group.
asChildbooleanfalseMerge onto a single child.
classNamestring-Class names on the separator.
AttributeDescription
data-slotbutton-group-separator
data-orientation"horizontal" or "vertical"
roleseparator

buttonGroupVariants

Exported tv() recipe. Same orientation key as ButtonGroup.

buttonGroupVariants({ orientation: "vertical" });

ButtonGroupProps

Props of ButtonGroup: fieldset attributes plus orientation.

Accessibility

The root is a grouping fieldset. Give it an accessible name. It is not a composite widget: each button, input, or menu trigger stays in the normal tab order.

  • Icon-only buttons: aria-label on the button. Decorative icons: aria-hidden="true".
  • Nested groups: name the outer group and each inner cluster when they represent different tasks.
  • ButtonGroupText is not a substitute for a field label; pair inputs with aria-label or Field.

Keyboard support

KeyDescription
TabMove to the next control in the group.
Shift + TabMove to the previous control.
Enter / SpaceActivate the focused button (or the child’s own keys, such as a menu trigger).