Components

Toggle Group

Related options in one connected row, with exactly one selected: to switch views or filter results. The first option sits at the inline start, the right in Arabic.

Preview

Mirrored is the same English content laid out right to left: it separates a mirroring bug from a translation one.

Installation

SoonInstalling from the registry opens at launch. Until then the command below is what it will be.
npx shadcn@latest add @ritla/toggle-group

Dependencies it brings: @base-ui/react, @phosphor-icons/react

Usage

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
<ToggleGroup aria-label="Period" defaultValue="week">
  <ToggleGroupItem value="day" label="Day" />
  <ToggleGroupItem value="week" label="Week" />
  <ToggleGroupItem value="month" label="Month" />
</ToggleGroup>

Examples

Styles

outline (default), tonal and filled. The selected option takes the pressed Button fill; dividers and the outline share one color. A disabled option stays visible.

Sizes

Button's heights: 56, 48, 40 and 32px. The tap target stays at least 48px at sm and xs.

Icons only

showLabel={false} leaves the icon alone for compact toolbars; label stays the name screen readers announce.

Filter and keyboard

A controlled version with value and onValueChange; the counts follow the numerals setting. Tab enters the group once and the arrows move between options: in Arabic, ArrowLeft is next.

API reference

ToggleGroup

PropTypeDefaultDescription
variant"outline" | "tonal" | "filled""outline"Figma's Style.
size"lg" | "md" | "sm" | "xs""lg"Figma's Size: 56, 48, 40 and 32px.
valuestringThe selected option's value (controlled).
defaultValuestringThe option selected at first. In Figma, the first one.
onValueChange(value: string) => voidCalled with the new value. Pressing the selected option again keeps it selected.
disabledbooleanfalseDisables every option.
aria-labelstringThe group's name (“Period”, “View”).

ToggleGroupItem

PropTypeDefaultDescription
valuestringIdentifies the option.
labelstringRequired. Figma's Label, and the option's name even when the text is hidden.
showLabelbooleantrueFigma's Show Label.
iconLeadingIconComponentIcon leading: an icon before the label.
disabledbooleanfalseState = Disabled for this option.

Accessibility

Keyboard

Keys are written for left-to-right. In right-to-left the arrow keys swap: the one pointing toward the reading direction moves forward.

KeyAction
TabEnters the group once, on the selected option; the next press leaves it.
ArrowRightArrowLeftMoves to the next or previous option and wraps at the ends. Flips in Arabic.
EnterSpaceSelects the focused option.

ARIA

  • A role="group" named by aria-label; each option is a native button with aria-pressed.
  • Selection doesn't rely on color alone: aria-pressed announces it, and one option always stays selected.
  • In icons-only mode, label becomes the option's aria-label.

Arabic and RTL notes

Mirrors

  • Option order mirrors: the first sits at the inline start (the right in Arabic), and dividers are logical borders that follow the direction.
  • Arrow keys follow the reading direction: ArrowLeft moves to the next option in Arabic.

Does not mirror

  • Non-directional icons (list, grid, map) don't mirror; only directional ones flip through <Icon>.
Numerals
Counts inside options (“Open 12”) are formatted with formatNumber and follow the numerals setting.
Arabic typography
body-16 (body-14 at xs) Semibold like Button. Options hug their text, so shorter Arabic labels aren't stretched.
Mixed-direction text
A Latin name at the end of an Arabic option (“عرض Kanban”) renders correctly; in the middle, wrap it in <bdi>.