Skip to content
Component installs need the registry setup— the Base UI shadcn project, the @vegastack namespace and the Cloudflare Access service token.
VegaStack Design

Toggle Group

A set of joined toggle buttons sharing one selection — single or multiple selection, three sizes, horizontal or vertical layout, full keyboard navigation.

Status
stable
Since
0.1.0
Accessibility pattern
APG toolbar of toggle buttons

Last updated

Install

Add Toggle Group from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.

pnpm dlx shadcn@latest add @vegastack/toggle-group

The same command installs the registry items it composes: @vegastack/toggle.

Usage

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";

<ToggleGroup defaultValue={["bold"]} aria-label="Text formatting">
  <ToggleGroupItem value="bold">Bold</ToggleGroupItem>
  <ToggleGroupItem value="italic">Italic</ToggleGroupItem>
  <ToggleGroupItem value="underline">Underline</ToggleGroupItem>
</ToggleGroup>;

Anatomy

Toggle Group is a compound component. Every exported part, with the data-slot it renders (generated from the canonical source):

ToggleGroup — data-slot="toggle-group"
ToggleGroupItem — data-slot="toggle-group-item"

Examples

Anatomy

ToggleGroup is a compound component built on Base UI Toggle Group. Compose the items inside the root; its visible outer hairline keeps the joined options legible at rest, while size set once on the root flows to every item via context. Selection is an array of item values:

<ToggleGroup defaultValue={["center"]} orientation="horizontal">
  <ToggleGroupItem value="left">
    <Icon />
  </ToggleGroupItem>
  <ToggleGroupItem value="center">
    <Icon />
  </ToggleGroupItem>
  <ToggleGroupItem value="right">
    <Icon />
  </ToggleGroupItem>
</ToggleGroup>
  • ToggleGroup — the root that owns the shared selection and orientation (data-slot="toggle-group", data-size, data-orientation, data-multiple when multi-select). Controlled via value/onValueChange (an array of pressed values), or uncontrolled via defaultValue. Pass multiple to allow more than one item pressed at a time.
  • ToggleGroupItem — a single toggle button (data-slot="toggle-group-item"). Identify it with value; pressed state is exposed as data-pressed and announced via aria-pressed. Inherits the group's size from context unless overridden. The first and last items round the group's outer corners so the buttons read as one joined control.

Selection

By default a Toggle Group is single-select — pressing an item unpresses the others, like a radio group. The selected value is the first (and only) entry in the array.

Pass multiple for multiple selection — each item presses independently, like a set of checkboxes. Common for formatting toolbars where bold, italic, and underline can all be active at once.

Sizes

size (sm / default / lg) is set once on the root and flows to every item. Items render flush inside one visible border-border boundary with shared rounded ends, so the group reads as a single control even when nothing is selected; the pressed item takes the same evident neutral fill as a standalone Toggle.

<ToggleGroup size="lg" aria-label="Text alignment">
  <ToggleGroupItem value="left">
    <AlignLeft />
  </ToggleGroupItem>
  <ToggleGroupItem value="center">
    <AlignCenter />
  </ToggleGroupItem>
  <ToggleGroupItem value="right">
    <AlignRight />
  </ToggleGroupItem>
</ToggleGroup>

Orientation

Set orientation="vertical" on the root to stack the items into a column — the joined corners round the top of the first item and the bottom of the last. Keyboard navigation switches to the up/down arrow keys automatically.

<ToggleGroup orientation="vertical" aria-label="View">
  <ToggleGroupItem value="list">List</ToggleGroupItem>
  <ToggleGroupItem value="grid">Grid</ToggleGroupItem>
</ToggleGroup>

States

Pass disabled on the root to disable the whole group — it flows to every item, dims the group via disabled:opacity-(--opacity-dim), and skips it for pointer and keyboard interaction. (A single item can be disabled the same way — see the Selection example above.)

Playground

Try both orientations, the three sizes, and single vs multiple selection, then copy the generated JSX.

<ToggleGroup aria-label="Text alignment">
  <ToggleGroupItem value="left" aria-label="Align left">
    <AlignLeft />
  </ToggleGroupItem>
  <ToggleGroupItem value="center" aria-label="Align center">
    <AlignCenter />
  </ToggleGroupItem>
  <ToggleGroupItem value="right" aria-label="Align right">
    <AlignRight />
  </ToggleGroupItem>
</ToggleGroup>

API Reference

ToggleGroup

PropTypeDefaultDescription
defaultValuereadonly string[]The initially pressed items as an array of their values. Uncontrolled counterpart of value.
multiplebooleanfalseAllow multiple items to be pressed at once. When false, pressing an item unpresses the others (single-select, radio-like).
onValueChange((value: string[]) => void)Fired when the pressed items change, with the next array of pressed values.
orientation"horizontal" | "vertical"'horizontal'Layout flow. horizontal joins items left-to-right; vertical stacks them. Also drives the arrow-key axis for keyboard navigation.
size"lg" | "md" | "sm"'md'Control height/density applied to every item — mirrors the Button scale.
valuereadonly string[]The pressed items as an array of their values. Controlled counterpart of defaultValue — pair with onValueChange. In single-select mode the array holds at most one value.

Data attributes and CSS variables on ToggleGroup

AttributeValues
data-size"md"
data-slot"toggle-group"

ToggleGroupItem

PropTypeDefaultDescription
size"lg" | "md" | "sm"'md'Control height/density. Defaults to the group's size (set via context); set here only to override a single item.

Data attributes and CSS variables on ToggleGroupItem

AttributeValues
data-sizemirrors a prop or state value
data-slot"toggle-group-item"

Accessibility

  • Built on Base UI Toggle Group: each item renders as a <button> with aria-pressed reflecting its state; the pressed state is also exposed as data-pressed for styling.
  • Always pass an aria-label (or aria-labelledby) to the group so assistive tech announces its purpose — icon-only items should each carry their own aria-label too.
  • Roving tabindex — the group is a single tab stop; arrow keys move focus between items and wrap around (loopFocus, on by default).
  • Items carry no custom ring class and never set outline: none, so keyboard focus shows the design system's global 2px :focus-visible ring (outline-ring). On focus an item raises its stacking order (focus-visible:z-10) so that ring is painted on top and never clipped by the adjacent joined buttons.
  • A disabled group (or item) sets disabled on the underlying button, is skipped by pointer and keyboard interaction, and is dimmed via disabled:opacity-(--opacity-dim).
KeyAction
TabMove focus into the group (lands on the first or pressed item), then out.
/ Move focus between items (horizontal orientation).
/ Move focus between items (vertical orientation).
Home / EndMove focus to the first / last item.
Enter / SpaceToggle the focused item.
ContractStates tested
Behaviourdefault, pressed
Accessibilityfocus-visible, labeled
Visualdefault, focus

Do / Don't

Do
Add an aria-label to the group and to each icon-only item, use single-select for mutually exclusive choices (alignment) and multiple for independent ones (bold/italic/underline).
Don't
Use a Toggle Group for navigation (use Tabs) or for a single on/off control (use a standalone Toggle or Switch).

On this page