ToggleGroup

Selects among two to five options in a segmented control with a visible active choice.

import { ToggleGroup } from "@usefragments/ui";Source

Preview

<ToggleGroup aria-label="Alignment" value={value} onValueChange={setValue}>
  <ToggleGroup.Item value="left">Left</ToggleGroup.Item>
  <ToggleGroup.Item value="center">Center</ToggleGroup.Item>
  <ToggleGroup.Item value="right">Right</ToggleGroup.Item>
</ToggleGroup>

Usage

Use when

  • Switching between mutually exclusive views or modes
  • Choosing one of two to five options
  • View switchers, segmented filters and the colour-mode control
  • A short row of formatting toggles (multiple)

Avoid when

  • More than five options — use Select or RadioGroup
  • Moving between pages or panels — use Tabs
  • A single on/off — use Switch or a toggle IconButton
  • A form field the user submits — use RadioGroup or a Checkbox group

Implementation notes

  • Two to five items
  • Labels short enough to scan in one pass
  • Icons work for well-known modes like grid and list; an icon beside a label is fine
  • One look: the band track with the chosen segment on the lifted thumb
  • Name the group with aria-label or aria-labelledby
  • The colour-mode control is a composition: a ToggleGroup wired to useTheme, System first

Installation

pnpm add @usefragments/ui

Import styles once from @usefragments/ui/styles. See the installation guide for framework and theme setup.

Anatomy

Import ToggleGroup once, then compose its parts with dot notation.

  • ToggleGroup.Item

One option at a time on the band track.

<ToggleGroup aria-label="Alignment" value={value} onValueChange={setValue}>
  <ToggleGroup.Item value="left">Left</ToggleGroup.Item>
  <ToggleGroup.Item value="center">Center</ToggleGroup.Item>
  <ToggleGroup.Item value="right">Right</ToggleGroup.Item>
</ToggleGroup>

Examples

Uncontrolled

defaultValue when nothing outside needs the selection.

<ToggleGroup aria-label="Layout" defaultValue="list">
  <ToggleGroup.Item value="grid">Grid</ToggleGroup.Item>
  <ToggleGroup.Item value="list">List</ToggleGroup.Item>
</ToggleGroup>

Multiple

Several toggles may stay pressed; the value is a string array.

<ToggleGroup aria-label="Formatting" multiple defaultValue={['bold']}>
  <ToggleGroup.Item value="bold">Bold</ToggleGroup.Item>
  <ToggleGroup.Item value="italic">Italic</ToggleGroup.Item>
  <ToggleGroup.Item value="underline">Underline</ToggleGroup.Item>
</ToggleGroup>

Sizes

xs in dense inspectors, lg where the control anchors a page.

import { Stack } from '@usefragments/ui';

<Stack gap="md" align="start">
  <ToggleGroup aria-label="Extra small" size="xs" defaultValue="a">
    <ToggleGroup.Item value="a">Day</ToggleGroup.Item>
    <ToggleGroup.Item value="b">Week</ToggleGroup.Item>

View Switcher

Icon-only modes, each named for screen readers.

import { List, SquaresFour } from '@phosphor-icons/react';

<ToggleGroup aria-label="View" value={view} onValueChange={setView} size="sm">
  <ToggleGroup.Item value="grid" aria-label="Grid view">
    <SquaresFour aria-hidden />
  </ToggleGroup.Item>

Colour Mode

The colour-mode control: a ToggleGroup wired to useTheme, System first.

import { Monitor, Moon, Sun } from '@phosphor-icons/react';
import { useTheme, type ThemeMode } from '@usefragments/ui';

const { mode, setMode } = useTheme();

<ToggleGroup

Full Width

The track fills its container and the segments share it.

<ToggleGroup aria-label="Billing" fullWidth defaultValue="monthly">
  <ToggleGroup.Item value="monthly">Monthly</ToggleGroup.Item>
  <ToggleGroup.Item value="yearly">Yearly</ToggleGroup.Item>
</ToggleGroup>

Disabled Item

One option locked out; the rest stay reachable.

<ToggleGroup aria-label="Tier" value={value} onValueChange={setValue}>
  <ToggleGroup.Item value="basic">Basic</ToggleGroup.Item>
  <ToggleGroup.Item value="pro">Pro</ToggleGroup.Item>
  <ToggleGroup.Item value="enterprise" disabled>Enterprise</ToggleGroup.Item>
</ToggleGroup>

API reference

Component props
Prop
Type
Default
Description
childrenRequiredReact.ReactNodeNot setToggleGroup.Item components
defaultValuestring | string[] | undefinedNot setInitial value (uncontrolled): a string, or a string array with multiple
disabledboolean | undefinedfalseDisable every option
fullWidthboolean | undefinedfalseStretch the track to its container; segments share the width
Item.childrenRequiredReact.ReactNodeNot setItem content: a label, a glyph, or both. An icon-only item needs `aria-label`.
Item.disabledboolean | undefinedNot setDisabled state
Item.valueRequiredstringNot setValue for this item
multipleboolean | undefinedfalseLet several options stay pressed; the value becomes a string array
onValueChange((value: string) => void) | ((value: string[]) => void) | undefinedNot setCalled with the next value: (value: string) => void, or (value: string[]) => void with multiple. A chosen segment stays chosen when pressed again in single mode.
size"xs" | "sm" | "md" | "lg" | undefinedxssmmdlg"md"Size on the one track: xs 24, sm 28, md 32, lg 40. Defaults to the nearest ComponentDefaultsProvider controlSize.
valuestring | string[] | undefinedNot setChosen value (controlled): a string, or a string array with multiple

Accessibility

  • The group has role group; each item is a toggle button with aria-pressed
  • Arrow keys move between items, Tab enters and leaves the group
  • Icon-only items still need an aria-label
  • Focus is drawn by the shared focus ring on top of its neighbours
  • RadioGroupalternativeRadioGroup for form-style single selection
  • TabsalternativeTabs for content panel switching
  • SwitchsiblingSwitch for single on/off control
  • ThemesiblingThe colour-mode control is a ToggleGroup wired to useTheme

Next steps