Skip to main content

Toggle Group

Segmented control for switching between related options. Supports single and multiple selection, three variants and sizes, icon-only buttons, vertical layout, and full keyboard navigation with roving tabindex — following the WAI-ARIA radiogroup and group patterns.

Live Preview

Interact with the ToggleGroup component in real time. Switch modes, sizes, and variants to see the segmented row adapt.

Live Code
tsx
<ToggleGroup
  ariaLabel="Text alignment"
  mode="single"
  size="md"
  variant="default"
  options={alignmentOptions}
  value={"center"}
  onChange={setValue}
/>

Installation

Install the Aidash Components package using your preferred package manager.

$ pnpm add @aidash/components

Import

Import the ToggleGroup component into your file.

tsx
import { ToggleGroup } from '@/components/aidash/toggle-group';

Basic Usage

Single-selection segmented control with three text-only options.

Basic Usage
tsx
const [range, setRange] = useState('week');

<ToggleGroup
  ariaLabel="Date range"
  mode="single"
  options={[
    { value: 'day', label: 'Day' },
    { value: 'week', label: 'Week' },
    { value: 'month', label: 'Month' },
  ]}
  value={range}
  onChange={(v) => setRange(v as string)}
/>

Multiple Selection

Set mode="multiple" to let users select any number of options. Icon-only buttons must provide ariaLabel.

value = ["bold"]
tsx
1const [formatting, setFormatting] = useState<string[]>(['bold']);2 3<ToggleGroup4  ariaLabel="Text formatting"5  mode="multiple"6  options={[7    { value: 'bold', icon: TextBoldIcon, ariaLabel: 'Bold' },8    { value: 'italic', icon: TextItalicIcon, ariaLabel: 'Italic' },9    { value: 'underline', icon: TextUnderlineIcon, ariaLabel: 'Underline' },10  ]}11  value={formatting}12  onChange={(v) => setFormatting(v as string[])}13/>

Variants

Three visual styles. Default uses a subtle inset pill; outline shares a bordered container; ghost has no chrome and only shows the active tint.

Default
Outline
Ghost
tsx
1<ToggleGroup variant="default" options={alignmentOptions} ... />2<ToggleGroup variant="outline" options={alignmentOptions} ... />3<ToggleGroup variant="ghost" options={alignmentOptions} ... />

Sizes

Three sizes to fit different layout contexts.

tsx
1<ToggleGroup size="sm" options={alignmentOptions} value="left" />2<ToggleGroup size="md" options={alignmentOptions} value="center" />3<ToggleGroup size="lg" options={alignmentOptions} value="right" />

Vertical Orientation

Stack the buttons vertically. Arrow-key navigation follows the axis (Up/Down).

tsx
1<ToggleGroup2  ariaLabel="View"3  orientation="vertical"4  options={[5    { value: 'board', label: 'Board' },6    { value: 'list', label: 'List' },7    { value: 'calendar', label: 'Calendar' },8  ]}9  value={view}10  onChange={(v) => setView(v as string)}11/>

Disabled State

Disable the whole group with the disabled prop, or disable individual options via option.disabled.

tsx
1<ToggleGroup disabled options={alignmentOptions} value="center" />2 3<ToggleGroup4  options={[5    { value: 'left', label: 'Left', icon: AlignLeftIcon },6    { value: 'center', label: 'Center', icon: AlignCenterIcon },7    { value: 'right', label: 'Right', icon: AlignRightIcon, disabled: true },8    { value: 'justify', label: 'Justify', icon: TextAlignJustifyCenterIcon },9  ]}10  value="center"11/>

Accessibility

Follows the WAI-ARIA radiogroup pattern in single mode and the group pattern in multiple mode.

role="radiogroup" vs role="group"

Single mode renders the root as role="radiogroup"; multiple mode uses role="group". An ariaLabel is required to name the group.

aria-checked / aria-pressed

In single mode each button carries role="radio" + aria-checked; in multiple mode toggles expose aria-pressed.

Keyboard Navigation

Arrow keys (Left/Right for horizontal, Up/Down for vertical) move focus between non-disabled options; in single mode arrow keys also select. Home / End jump to the first / last non-disabled option. Space and Enter toggle in multiple mode.

Roving Tabindex

Only the currently selected (or first non-disabled) option is in the tab order (tabIndex=0); the rest use -1, so a single Tab press moves in and out of the whole group.

tsx
1{/* Single mode → renders <div role="radiogroup"> with role="radio" children */}2<ToggleGroup3  ariaLabel="Text alignment"4  mode="single"5  options={alignmentOptions}6  value={alignment}7  onChange={(v) => setAlignment(v as string)}8/>9 10{/* Multiple mode → renders <div role="group"> with aria-pressed buttons */}11<ToggleGroup12  ariaLabel="Text formatting"13  mode="multiple"14  options={formattingOptions}15  value={formatting}16  onChange={(v) => setFormatting(v as string[])}17/>

API Reference

Complete list of props for the ToggleGroup component.

ToggleGroupProps
PropTypeDefaultDescription
optionsToggleGroupOption[]Array of options to render as toggle buttons.
valuestring | string[]Selected value. String in single mode, string[] in multiple mode.
onChange(value: string | string[]) => voidFires when selection changes. Shape mirrors value.
mode'single' | 'multiple''single'Whether only one or many options can be selected at the same time.
size'sm' | 'md' | 'lg''md'Size preset for the segmented row.
variant'default' | 'outline' | 'ghost''default'Visual style of the container and active state.
orientation'horizontal' | 'vertical''horizontal'Layout direction; also flips the arrow-key axis.
disabledbooleanfalseDisable the entire group.
allowDeselectbooleanfalseIn single mode, allow clicking the selected option again to clear it.
ariaLabelstringAccessible label for the group (radiogroup / group).
classNamestring''Additional classes appended to the root container.
Note
The value and onChange types depend on mode: single mode uses a plain string, while multiple mode uses string[]. Narrow the type at the call site (e.g. v as string[]) when reading the callback value.

Other components that work well alongside Toggle Group.