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.
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 bordersWith spacing
spacing=1 — individual bordersComposition 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)
| Orientation | First item | Middle items | Last item |
|---|---|---|---|
horizontal | rounded-l-lg | rounded-none border-l-0 | rounded-r-lg |
vertical | rounded-t-lg | rounded-none border-t-0 | rounded-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-pressedautomatically. Screen readers announce items as "Bold toggle, pressed" or "Bold toggle, not pressed". data-orientationis exposed on the container, and arrow key direction follows: left/right for horizontal, up/down for vertical.- Focused items receive
z-10so 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
variantandsizeare passed from the group to items via React context. Items use the group value unless they specify their own override.spacingcontrols the CSS variable--gapon the container. When 0, border-collapse CSS kicks in.orientationmaps todata-orientationand controls flex direction (flex-rowvsflex-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>