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.
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-groupCopies 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.jsonWorks, 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.
| Prop | Type | Default |
|---|---|---|
selectionModeOne pressed item at most, or any number. | "single" | "multiple" | "single" |
orientationDirection of the group. Arrow keys follow it. | "horizontal" | "vertical" | "horizontal" |
sizeItem height, from the shared control sizes. | "sm" | "md" | "lg" | "md" |
variantOutline draws a border around every item; ghost has no border until an item is hovered or pressed. | "outline" | "ghost" | "outline" |
isDisabledDisables every item. | boolean | false |
disallowEmptySelectionKeeps one item pressed at all times. | boolean | false |
childrenToggleGroupItem elements, each with an id. Control the pressed ids with selectedKeys, defaultSelectedKeys and onSelectionChange. | node | none |
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