ToggleGroup

A group of toggle buttons where one or multiple can be active at a time. Built on @base-ui/react's ToggleGroup primitive with roving tabindex keyboard navigation, shared variant/size context, optional spacing, and horizontal or vertical orientation.

Anatomy

A <ToggleGroup> wraps multiple <ToggleGroupItem>children. The group manages selection state (single or multiple) and passes variant/size context down via React context so items inherit the group's styling. Uses data-slot="toggle-group" and data-orientation for CSS selection. Items use data-slot="toggle-group-item" with data-variant and data-size reflecting inherited values.

data-slot="toggle-group"data-orientationdefaultValue

Selection Modes

ToggleGroup supports both single selection (radio-like, one active at a time) and multiple selection (checkbox-like, any combination). Pass multiple to the group to allow several items to be active simultaneously.

Single selection

Alignment, view mode, sort direction

Multiple selection

Text formatting, filter tags, feature toggles

Variants

Pass variant to the group to set the style for all items. Individual items can override with their own variant prop if needed.

variant="default"
variant="outline"
variant="secondary"
variant="ghost"

Orientation

Set orientation="vertical" to stack items vertically. Arrow key navigation follows the orientation automatically.

Horizontal (default)

Vertical

Spacing

By default, items are flush with shared borders (spacing=0). Set the spacingprop to add a gap between items. When spacing is 0, items share borders and have their intermediate radii removed. With spacing > 0, each item retains its full border-radius.

No spacing (default)

spacing=0 — shared borders

With spacing

spacing=1 — individual borders

Composition Patterns

Common patterns combining ToggleGroup with different configurations.

Text formatting toolbar

View switcher

List type selector

States

Individual items within a group manage their own pressed, focused, and disabled states. The group handles selection logic (toggling items on/off).

Active item

aria-pressed="true"

Focused item

focus-visible z-10

Disabled item

disabled, 50% opacity

Token Map

ToggleGroupItem inherits the same size tokens as Toggle. The group container adds border-collapse logic when spacing=0.

Group container (spacing=0)

OrientationFirst itemMiddle itemsLast item
horizontalrounded-l-lgrounded-none border-l-0rounded-r-lg
verticalrounded-t-lgrounded-none border-t-0rounded-b-lg

Design Guidelines

Do

  • Use for mutually exclusive options. Single-selection ToggleGroup is ideal for choosing one option from a small set (2-5 items) like alignment or view mode.
  • Provide aria-label on items. Each ToggleGroupItem needs an accessible label, especially for icon-only items.
  • Keep items visually consistent. Use the same variant and size for all items in a group. The group context handles this automatically when you set props on the parent.
  • Separate unrelated groups. Use a Separator or gap between logically distinct toggle groups (e.g., text style vs. alignment in a toolbar).

Don't

  • Don't use for more than 5-6 options. Large groups are hard to scan. Use a Select, RadioGroup, or other component for longer lists.
  • Don't mix icon-only and text items. Inconsistent item widths create visual imbalance. Either use all icons or all text labels within a group.
  • Don't use for navigation. ToggleGroup represents state, not navigation. Use Tabs or NavigationMenu for switching between views/pages.
  • Don't forget keyboard navigation. The roving tabindex is built-in — but if you wrap items in custom elements, ensure focus management isn't broken.

Developer Reference

Accessibility

  • Uses roving tabindex: Tab enters the group, then arrow keys move between items. This follows the WAI-ARIA toolbar pattern.
  • Each item manages aria-pressed automatically. Screen readers announce items as "Bold toggle, pressed" or "Bold toggle, not pressed".
  • data-orientation is exposed on the container, and arrow key direction follows: left/right for horizontal, up/down for vertical.
  • Focused items receive z-10 so the focus ring is never clipped by adjacent flush items.
  • Disabled items are skipped during keyboard navigation but remain visible in the layout.

Context Inheritance

  • variant and size are passed from the group to items via React context. Items use the group value unless they specify their own override.
  • spacing controls the CSS variable --gap on the container. When 0, border-collapse CSS kicks in.
  • orientation maps to data-orientation and controls flex direction (flex-row vs flex-col).

Usage

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"
import { Bold, Italic, Underline, AlignLeft, AlignCenter, AlignRight } from "lucide-react"

// Single selection (radio-like)
<ToggleGroup defaultValue={["center"]}>
  <ToggleGroupItem value="left" aria-label="Align left">
    <AlignLeft className="size-4" />
  </ToggleGroupItem>
  <ToggleGroupItem value="center" aria-label="Align center">
    <AlignCenter className="size-4" />
  </ToggleGroupItem>
  <ToggleGroupItem value="right" aria-label="Align right">
    <AlignRight className="size-4" />
  </ToggleGroupItem>
</ToggleGroup>

// Multiple selection (checkbox-like)
<ToggleGroup multiple defaultValue={["bold", "italic"]}>
  <ToggleGroupItem value="bold" aria-label="Bold">
    <Bold className="size-4" />
  </ToggleGroupItem>
  <ToggleGroupItem value="italic" aria-label="Italic">
    <Italic className="size-4" />
  </ToggleGroupItem>
  <ToggleGroupItem value="underline" aria-label="Underline">
    <Underline className="size-4" />
  </ToggleGroupItem>
</ToggleGroup>

// With variant and size
<ToggleGroup variant="outline" size="sm" defaultValue={["bold"]}>
  <ToggleGroupItem value="bold" aria-label="Bold">
    <Bold className="size-4" />
  </ToggleGroupItem>
</ToggleGroup>

// Vertical orientation
<ToggleGroup orientation="vertical" variant="outline" defaultValue={["grid"]}>
  <ToggleGroupItem value="list" aria-label="List view">...</ToggleGroupItem>
  <ToggleGroupItem value="grid" aria-label="Grid view">...</ToggleGroupItem>
</ToggleGroup>

// With spacing (individual borders)
<ToggleGroup variant="outline" spacing={1} defaultValue={["bold"]}>
  <ToggleGroupItem value="bold" aria-label="Bold">
    <Bold className="size-4" />
  </ToggleGroupItem>
  <ToggleGroupItem value="italic" aria-label="Italic">
    <Italic className="size-4" />
  </ToggleGroupItem>
</ToggleGroup>

// Controlled
const [value, setValue] = useState(["center"])
<ToggleGroup value={value} onValueChange={setValue}>
  ...
</ToggleGroup>