Toggle Group
Groups related Toggle buttons into one control with shared roving-focus
keyboard navigation — text alignment, view mode, formatting clusters. Built on
Base UI ToggleGroup.
Import
import { ToggleGroup } from "@photon-ai/pho-ui/components/toggle-group";
import { Toggle } from "@photon-ai/pho-ui/components/toggle";
Basic
Single-select by default — one item pressed at a time; pressing another releases
the first. Each Toggle carries a value; track selection with value /
onValueChange (an array) or defaultValue.
import {
IconAlignLeft,
IconAlignCenter,
IconAlignRight,
} from "@tabler/icons-react";
<ToggleGroup defaultValue={["left"]}>
<Toggle value="left" aria-label="Align left">
<IconAlignLeft />
</Toggle>
<Toggle value="center" aria-label="Align center">
<IconAlignCenter />
</Toggle>
<Toggle value="right" aria-label="Align right">
<IconAlignRight />
</Toggle>
</ToggleGroup>;
Multiple
Set multiple when several items can be pressed at once — a formatting cluster
where Bold, Italic, and Underline stack. The value is still an array; it simply
holds every pressed item.
import { IconBold, IconItalic, IconUnderline } from "@tabler/icons-react";
<ToggleGroup multiple defaultValue={["bold"]}>
<Toggle value="bold" aria-label="Bold">
<IconBold />
</Toggle>
<Toggle value="italic" aria-label="Italic">
<IconItalic />
</Toggle>
<Toggle value="underline" aria-label="Underline">
<IconUnderline />
</Toggle>
</ToggleGroup>;
Controlled
Pass value and onValueChange to own the selection — the callback hands back
the pressed-values array, so read value[0] for a single-select group and the
whole array when multiple. Use defaultValue instead for the initial value
only.
const [value, setValue] = useState<string[]>(["left"]);
<ToggleGroup value={value} onValueChange={setValue}>
{/* …toggles… */}
</ToggleGroup>;
Orientation
orientation="vertical" stacks the group and swaps the arrow-key axis to
Up/Down. It sets data-orientation for styling; lay the items out in a column
yourself (here, className="flex-col").
<ToggleGroup
orientation="vertical"
defaultValue={["left"]}
className="flex-col"
>
{/* …toggles… */}
</ToggleGroup>
Disabled
disabled on the group ignores interaction across every item and dims the whole
control; the current selection stays visible.
<ToggleGroup defaultValue={["center"]} disabled>
{/* …toggles… */}
</ToggleGroup>
Anatomy
- ToggleGroup — the roving-focus container (
role="group"); owns the selected-values array and themultiple/orientationbehavior. - Toggle — each two-state button, from
@photon-ai/pho-ui/components/toggle. Every child carries avaluethat keys it in the group’s array, and anaria-labelsince it’s icon-only.
Props
- value —
readonly string[]— controlled pressed values (the counterpart ofdefaultValue) - defaultValue —
readonly string[]— uncontrolled initial pressed values - onValueChange —
(value: string[], details) => void— fired when the pressed set changes - multiple —
boolean(defaultfalse) — allow more than one item pressed at once - disabled —
boolean(defaultfalse) — ignore interaction for the whole group - orientation —
horizontal·vertical(defaulthorizontal) — arrow-key axis - loopFocus —
boolean(defaulttrue) — wrap arrow-key focus at the ends - className —
string— merged onto the group<div>
Each child Toggle takes its own value (required) and a size of sm · md
· lg (default md) — see the Toggle page.
Accessibility
- Semantics — the group renders a
<div role="group">; eachToggleis a<button>that exposes its state througharia-pressed. Because the toggles here are icon-only, every one needs anaria-labelthat names its action. - Focus — roving: only one toggle is tabbable at a time, so the group reads
as one control rather than N buttons.
orientationsets both the arrow-key axis and thedata-orientationattribute.
Keyboard: the group is a single tab stop (roving tabindex).
- Tab — move focus into the group, onto the active/first toggle, then out
- Arrow keys — move focus between toggles (Left/Right when
horizontal, Up/Down whenvertical); focus wraps at the ends whileloopFocusis on - Enter / Space — toggle the focused item
Best practices
- Reach for a toggle group when the options are mutually related and one
selection is the norm (alignment, view mode). For independent on/off
settings, use separate
Switches. - Set
multipleonly when several can be active together (e.g. Bold + Italic); otherwise keep the default single-select. - Each
Toggleis icon-only, so anaria-labelper item is required — name the action (Align left), not the glyph. - Keep the item count small and the icons unambiguous; a toggle group is a glanceable control, not a menu.