Skip to main content
Graphite UI

Button Group

A row of related actions that allows at most one primary. A single button needs no group, and a set of choices between values is a radio button group, not this.

Contract 1.0.1No kit page

Live preview

Rendered by the component itself from the same generated tokens as the rest of the site.

Installation

Install it with the shadcn CLI, along with everything it uses. New project? Set it up first, as Quick start shows.

npx shadcn@latest add https://www.graphite-ui.com/r/button-group.json
import { ButtonGroup } from '@/components/ui/button-group'

Anatomy

One slot, two or more Buttons. The group adds a gap from the spacing scale and no colour of its own: every visual decision belongs to the buttons inside it.

  1. ActionsRequiredTwo or more Buttons. A group of one is a Button, and needs no wrapper.

API reference

Generated from the contract, versioned with it, and checked by drift-check. This table cannot describe props the component does not have.

Button Group props
PropTypeDefaultNotes
className——Merged after the group class, so a caller can extend without forking.

Design tokens

Every swatch is live. Change the source color in the header and this table repaints, because it reads the same roles the component does.

  • spacingGap between actions.

Usage

The contract's prohibitions, written as the choices you will actually face.

Do

  • Wrap every footer or toolbar in a group, so the one-primary rule is checked instead of remembered.
  • Pass the Buttons as direct children or inside a fragment. Fragments are unwrapped before the count, so a footer passed as <>…</> is still checked.
  • Leave the group with no primary when no action is clearly the main one.
  • Stack actions vertically in the container’s own layout when a narrow space needs it.

Don’t

  • Add a second primary. It throws at render, naming the count, and that is the intended outcome.
  • Wrap a Button in a div or span. The check does not look inside other elements, so a second primary in there slips past it and the rule is still broken.
  • Override the gap on one instance. Two footers in one product should not disagree about spacing.
  • Use a group for a single button. A group of one is just a Button.

Accessibility

What the component does for you, and what it leaves to you.

Roles
A plain div with no role. When the actions need a shared name, pass role="group" and an aria-label: both spread to the element.
Keyboard
Each Button stays a separate tab stop in source order. The group adds no arrow-key navigation and traps nothing.
Order
The row never reverses, so the order a screen reader announces is the order a sighted reader sees.
Focus
Each Button draws its own focus ring. The group draws nothing and clips nothing.
Emphasis
With at most one primary, a reader scanning by visual weight finds one main action, not two competing ones.