Skip to content

Toggle Group

A row or column of joined toggle buttons with shared borders, for formatting options or a choice between a few modes. Pick single or multiple selection.

Inputv0.1.0experimentalWCAG 2.2 AAView spec
import { ToggleGroup, ToggleGroupItem } from "@rdloom/react";

export default function ToggleGroupFormattingExample() {
  return (
    <div className="flex w-full justify-center">
      <ToggleGroup aria-label="Text formatting" selectionMode="multiple" defaultSelectedKeys={["bold"]}>
        <ToggleGroupItem id="bold">Bold</ToggleGroupItem>
        <ToggleGroupItem id="italic">Italic</ToggleGroupItem>
        <ToggleGroupItem id="underline">Underline</ToggleGroupItem>
        <ToggleGroupItem id="strike">Strikethrough</ToggleGroupItem>
      </ToggleGroup>
    </div>
  );
}

Installation

npx rdloom add toggle-group

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/toggle-group.json

Works, but without upgrade tracking.

Usage

import { ToggleGroup, ToggleGroupItem } from "@rdloom/react";

<div className="flex w-full justify-center">
  <ToggleGroup aria-label="Text formatting" selectionMode="multiple" defaultSelectedKeys={["bold"]}>
    <ToggleGroupItem id="bold">Bold</ToggleGroupItem>
    <ToggleGroupItem id="italic">Italic</ToggleGroupItem>
    <ToggleGroupItem id="underline">Underline</ToggleGroupItem>
    <ToggleGroupItem id="strike">Strikethrough</ToggleGroupItem>
  </ToggleGroup>
</div>

Alignment

import { MenuIcon, ToggleGroup, ToggleGroupItem } from "@rdloom/react";

export default function ToggleGroupAlignmentExample() {
  return (
    <div className="flex w-full justify-center">
      <ToggleGroup aria-label="Text alignment" selectionMode="single" defaultSelectedKeys={["start"]} disallowEmptySelection variant="ghost">
        <ToggleGroupItem id="start" aria-label="Align to start">
          <MenuIcon />
        </ToggleGroupItem>
        <ToggleGroupItem id="center" aria-label="Align to center">
          <MenuIcon className="size-4 shrink-0 scale-x-75" />
        </ToggleGroupItem>
        <ToggleGroupItem id="end" aria-label="Align to end">
          <MenuIcon className="size-4 shrink-0 -scale-x-100" />
        </ToggleGroupItem>
      </ToggleGroup>
    </div>
  );
}

Vertical

import { ToggleGroup, ToggleGroupItem } from "@rdloom/react";

export default function ToggleGroupVerticalExample() {
  return (
    <div className="flex w-full justify-center">
      <ToggleGroup aria-label="Density" orientation="vertical" defaultSelectedKeys={["comfortable"]} disallowEmptySelection>
        <ToggleGroupItem id="compact">Compact</ToggleGroupItem>
        <ToggleGroupItem id="comfortable">Comfortable</ToggleGroupItem>
        <ToggleGroupItem id="spacious">Spacious</ToggleGroupItem>
      </ToggleGroup>
    </div>
  );
}

Disabled

import { ToggleGroup, ToggleGroupItem } from "@rdloom/react";

export default function ToggleGroupDisabledExample() {
  return (
    <div className="flex w-full justify-center">
      <ToggleGroup aria-label="Layout (locked by your plan)" isDisabled defaultSelectedKeys={["grid"]}>
        <ToggleGroupItem id="list">List</ToggleGroupItem>
        <ToggleGroupItem id="grid">Grid</ToggleGroupItem>
        <ToggleGroupItem id="board">Board</ToggleGroupItem>
      </ToggleGroup>
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
selectionMode

One pressed item at most, or any number.

"single" | "multiple""single"
orientation

Direction of the group. Arrow keys follow it.

"horizontal" | "vertical""horizontal"
size

Item height, from the shared control sizes.

"sm" | "md" | "lg""md"
variant

Outline draws a border around every item; ghost has no border until an item is hovered or pressed.

"outline" | "ghost""outline"
isDisabled

Disables every item.

booleanfalse
disallowEmptySelection

Keeps one item pressed at all times.

booleanfalse
children

ToggleGroupItem elements, each with an id. Control the pressed ids with selectedKeys, defaultSelectedKeys and onSelectionChange.

nodenone

Accessibility

Role group, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

  • Tab moves focus into the group and out of it; only one item is in the tab order
  • Arrow keys move focus between items, following the orientation
  • Space or Enter toggles the focused item
  • Home and End move to the first and last item

Screen readers announce

  • Items are announced as pressed or not pressed, or as checked radios in single mode
  • A disabled item is announced as dimmed or unavailable

What your code must do

  • The group has an accessible name through aria-label or aria-labelledby
  • In single mode the group is a radiogroup and items are radios; in multiple mode items expose aria-pressed
  • An icon-only item has an aria-label
  • The pressed state is shown by fill and weight, not only by color
  • Joined borders keep a 3:1 contrast against the background

Guidelines

Use it when

  • A formatting toolbar where several options can be on
  • Alignment or view mode where one of a few is on
  • A compact choice with icons

Avoid it when

  • Switching the content of a panel: use Tabs or SegmentedControl
  • One on or off setting: use Switch
  • More than about six options: use Select

Don't

  • Icon-only items with no aria-label
  • A group with no accessible name
  • Mixing text and icon items in one group

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-surface-default
  • --rd-color-surface-subtle
  • --rd-color-border-default
  • --rd-color-border-strong
  • --rd-color-action-primary
  • --rd-color-text-default
  • --rd-color-focus-ring
  • --rd-radius-control