Skip to content

Toggle Group

Compound toggle button group with keyboard navigation for single or multiple selection.

Preview

Installation

$pnpm exec dgadd add ui/toggle-group
[Installs to]src/components/ui/toggle-group[Item]ui/toggle-group

dgadd is not public on npm yet. Until the first release, pack @diffgazer/add from the repository and install that tarball into this app, which is what puts dgadd on pnpm exec.

UI components require Tailwind CSS v4. Local copy mode imports src/styles/styles.css; package mode uses @diffgazer/ui CSS once packages are available.

Usage

tsx
"use client";import { useState } from "react";import { ToggleGroup } from "@/components/ui/toggle-group";export default function ToggleGroupDefault() {  const [value, setValue] = useState<string | null>("all");  return (    <ToggleGroup label="File change filter" value={value} onChange={setValue}>      <ToggleGroup.Item value="all">All</ToggleGroup.Item>      <ToggleGroup.Item value="added">Added</ToggleGroup.Item>      <ToggleGroup.Item value="modified">Modified</ToggleGroup.Item>      <ToggleGroup.Item value="deleted">Deleted</ToggleGroup.Item>    </ToggleGroup>  );}

Examples

Variants

Preview

With Counts

Preview

Multiple Selection

Preview

Sizes

Preview

Vertical

Preview

Disabled

Preview

API Reference

ToggleGroup

NameTypeDefaultDescription
selectionMode"single" | "multiple""single"Switches between radio-style single selection and pressed-button-style multiple selection. Switches value/onChange/defaultValue from string|null to readonly string[].
valuestring | null | readonly string[]Controlled selected value(s). string|null for single mode, readonly string[] for multiple.
defaultValuestring | null | readonly string[]null (single) | [] (multiple)Initial selected value(s) for uncontrolled mode.
onChange((value: string | null) => void) | ((value: readonly string[]) => void)Fired when the selected value(s) change.
allowDeselectbooleanfalseSingle mode only. When true, clicking the active item deselects it (allowing a null value).
disabledbooleanfalseDisables the entire group.
size"sm" | "md""sm"Item density token. "sm" is 36px, rising to a 44px target on coarse pointers; "md" is always a 44px target.
variant"default" | "bracket" | "pill" | "underline""default"Item style: default outlines, bracket adds [ ], pill fills, underline rules.
orientation"horizontal" | "vertical""horizontal"Layout axis and arrow-key navigation direction.
wrapbooleantrueWhen true, arrow navigation wraps and horizontal items flex-wrap.
highlightedstring | nullControlled highlighted (focused) value for cross-component navigation.
onHighlightChange(value: string | null) => voidFired when the highlighted value changes.
onNavigationBoundaryReached(direction: "previous" | "next", event: KeyboardEvent, key: string) => voidFired when arrow navigation reaches the first/last item with wrap disabled.
labelstringAccessible name for the group container.
aria-labelledbystringID of the element labelling the group.
namestringSingle mode only. Form field name; renders a hidden input for native form submission.
childrenrequiredReactNodeToggleGroup.Item children.

ToggleGroup.Item

NameTypeDefaultDescription
valuerequiredstringStable identifier matched against the group value.
countnumberOptional trailing count rendered as [label count].
disabledbooleanfalseDisables this item only; removed from arrow navigation and focus.
childrenrequiredReactNodeItem label.

Data attributes

AttributeApplies toValuesDescription
data-stateToggleGroup.Item"on" | "off"Reflects whether the item is selected.
data-highlightedToggleGroup.Itempresent when highlightedMarks the roving-focus item.
data-valueToggleGroup.Itemitem valueExposes the item value used for selection and indicator positioning.
data-variantToggleGroup"default" | "pill" | "underline" | "bracket"Reflects the visual variant on the group root.
data-orientationToggleGroup"horizontal" | "vertical"Reflects the layout and arrow-key axis.

Accessibility

Keyboard Navigation

Arrow keys move focus between toggle items with wrapping. Single mode follows focus; multiple mode and allow-deselect single mode use button semantics and toggle with Enter or Space.

KeyAction
ArrowRight / ArrowDownMoves focus to the next enabled item; wraps when wrap is true.
ArrowLeft / ArrowUpMoves focus to the previous enabled item; wraps when wrap is true.
Home / EndMoves focus to the first or last enabled item.
Enter / SpaceToggles the focused item in multiple mode or allow-deselect single mode; native radio-style single mode changes selection during arrow navigation.

With keyboard navigation

Preview

Notes

Requires @diffgazer/keys (package mode)

ToggleGroup's arrow-key navigation imports from the required @diffgazer/keys peer. Package consumers must install @diffgazer/keys with @diffgazer/ui; packages are not yet published to npm, so until the first release install both from locally packed tarballs. Importing @diffgazer/ui/components/toggle-group without keys fails at module load with an error naming the missing @diffgazer/keys package. Copy/dgadd consumers do not need the package — copy mode rewrites the keyboard hooks to local source.

Compound Component

ToggleGroup uses compound sub-components (ToggleGroup.Item) rather than a data array. Each item accepts children for the label and an optional count prop that renders as [label count].

Composition Contract

Use ToggleGroup.Item as an explicit child in the ToggleGroup JSX tree. Custom item UI belongs inside ToggleGroup.Item. Components that create items internally from an opaque wrapper are not part of the current public contract.

Keyboard Navigation

Arrow keys move roving focus with wrapping by default. In single mode, selection follows focus unless allowDeselect is enabled; in multiple mode, arrow keys move focus and Enter/Space toggles the focused item. Highlight state can be controlled externally via highlighted and onHighlightChange props. Use onNavigationBoundaryReached for composite focus handoff when wrap is false.

Role semantics

Single mode without allowDeselect renders as radiogroup/radio with aria-checked and allows at most one active choice. Initial selection is optional: defaultValue=null starts with no radio checked. Multiple mode and allow-deselect single mode render as group plus button-style items with aria-pressed because each item is independently toggleable.

Source

Install via CLI: pnpm exec dgadd add ui/toggle-group. Keyboard hooks are included as standalone copies. For the full experience, use --integration keys.

Highlighted source loads after this disclosure opens. Browse the source repository.