1
ComponentsActions

Toggle Group

Related toggle buttons with shared single-select or multi-select state.

Purpose

Use Toggle Group for related toggle buttons that share single-select or multi-select state, such as formatting, alignment or view controls.

Segmented controls

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";

export function Example() {
  return (
    <ToggleGroup defaultValue={["week"]}>
      <ToggleGroupItem value="day">Day</ToggleGroupItem>
      <ToggleGroupItem value="week">Week</ToggleGroupItem>
    </ToggleGroup>
  );
}

Installation

With the Siteplane registry alias configured, add the component and import the installed source from your app.

CLI

pnpm dlx shadcn@4.16.1 add @siteplane/toggle-group

Import

import {
  ToggleGroup,
  ToggleGroupItem,
  ToggleGroupSeparator,
} from "@/components/ui/toggle-group";

Need the machine-readable source metadata? View registry JSON.

Usage

Toggle Group is a segmented control. Single-select gets the sliding indicator; multi-select keeps per-item state.

import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";

export function Example() {
  return (
    <ToggleGroup defaultValue={["week"]}>
      <ToggleGroupItem value="day">Day</ToggleGroupItem>
      <ToggleGroupItem value="week">Week</ToggleGroupItem>
    </ToggleGroup>
  );
}

Examples

Selection modes, sizes, orientation, separators, widths and form integration are props and compositions of the same primitive.

Multiple selection

With multiple, several items can be pressed at once and each keeps its own background.

<ToggleGroup aria-label="Text formatting" defaultValue={["bold"]} multiple>
  <ToggleGroupItem aria-label="Bold" value="bold"><BoldIcon /></ToggleGroupItem>
  <ToggleGroupItem aria-label="Italic" value="italic"><ItalicIcon /></ToggleGroupItem>
  <ToggleGroupItem aria-label="Underline" value="underline"><UnderlineIcon /></ToggleGroupItem>
</ToggleGroup>

Sizes

<ToggleGroup size="sm" defaultValue={["left"]}>...</ToggleGroup>
<ToggleGroup defaultValue={["left"]}>...</ToggleGroup>
<ToggleGroup size="lg" defaultValue={["left"]}>...</ToggleGroup>
<ToggleGroup size="xl" defaultValue={["left"]}>...</ToggleGroup>

Vertical

<ToggleGroup
  aria-label="View density"
  defaultValue={["comfortable"]}
  orientation="vertical"
  variant="outline"
>
  <ToggleGroupItem value="compact">Compact</ToggleGroupItem>
  <ToggleGroupItem value="comfortable">Comfortable</ToggleGroupItem>
  <ToggleGroupItem value="spacious">Spacious</ToggleGroupItem>
</ToggleGroup>

With separators

<ToggleGroup
  aria-label="Format with separators"
  defaultValue={["bold"]}
  multiple
  variant="outline"
>
  <ToggleGroupItem aria-label="Bold" value="bold"><BoldIcon /></ToggleGroupItem>
  <ToggleGroupItem aria-label="Italic" value="italic"><ItalicIcon /></ToggleGroupItem>
  <ToggleGroupSeparator />
  <ToggleGroupItem aria-label="Strikethrough" value="strike"><StrikethroughIcon /></ToggleGroupItem>
</ToggleGroup>

Icon + text

<ToggleGroup aria-label="Color theme" defaultValue={["system"]} variant="outline">
  <ToggleGroupItem value="light"><SunIcon />Light</ToggleGroupItem>
  <ToggleGroupItem value="dark"><MoonIcon />Dark</ToggleGroupItem>
  <ToggleGroupItem value="system"><MonitorIcon />System</ToggleGroupItem>
</ToggleGroup>

Disabled

<ToggleGroup
  aria-label="Text formatting disabled"
  defaultValue={["italic"]}
  disabled
  multiple
>
  <ToggleGroupItem aria-label="Bold disabled" value="bold"><BoldIcon /></ToggleGroupItem>
  <ToggleGroupItem aria-label="Italic disabled" value="italic"><ItalicIcon /></ToggleGroupItem>
  <ToggleGroupItem aria-label="Underline disabled" value="underline"><UnderlineIcon /></ToggleGroupItem>
</ToggleGroup>

Full width

With width="full" the group stretches to its container; grow items share the extra space.

<ToggleGroup
  aria-label="Billing interval"
  defaultValue={["monthly"]}
  variant="outline"
  width="full"
>
  <ToggleGroupItem grow value="monthly">Monthly</ToggleGroupItem>
  <ToggleGroupItem grow value="yearly">Yearly</ToggleGroupItem>
</ToggleGroup>

View switcher

Controlled selection that ignores empty updates so one view always stays active.

Layout
Active view: grid
const [view, setView] = useState<string[]>(["grid"]);

<ToggleGroup
  aria-labelledby={labelId}
  onValueChange={(next) => {
    if (next.length > 0) {
      setView(next);
    }
  }}
  value={view}
  variant="outline"
>
  <ToggleGroupItem value="grid"><LayoutGridIcon />Grid</ToggleGroupItem>
  <ToggleGroupItem value="list"><ListIcon />List</ToggleGroupItem>
</ToggleGroup>

Form integration

Alignment: not submitted
<Form onSubmit={onSubmit}>
  <Field>
    <FieldLabel id={labelId}>Choose alignment</FieldLabel>
    <ToggleGroup
      aria-labelledby={labelId}
      onValueChange={(next) => {
        if (next.length > 0) {
          setAlignment(next);
        }
      }}
      value={alignment}
      variant="outline"
    >
      <ToggleGroupItem aria-label="Align left" value="left"><AlignLeftIcon /></ToggleGroupItem>
      <ToggleGroupItem aria-label="Align center" value="center"><AlignCenterIcon /></ToggleGroupItem>
      <ToggleGroupItem aria-label="Align right" value="right"><AlignRightIcon /></ToggleGroupItem>
    </ToggleGroup>
  </Field>
  <Button loading={loading} type="submit">Submit</Button>
</Form>

Wrapping

wrap lets a long group break into several rows instead of overflowing.

<ToggleGroup aria-label="Report columns" className="max-w-64" multiple variant="outline" wrap>
  <ToggleGroupItem value="status">Status</ToggleGroupItem>
  <ToggleGroupItem value="owner">Owner</ToggleGroupItem>
  <ToggleGroupItem value="due">Due date</ToggleGroupItem>
  <ToggleGroupItem value="priority">Priority</ToggleGroupItem>
  <ToggleGroupItem value="labels">Labels</ToggleGroupItem>
</ToggleGroup>

Item padding and width

padding="comfortable" widens the item, maxWidth="label" truncates long labels.

{/* Wider hit area */}
<ToggleGroupItem padding="comfortable" value="day">Day</ToggleGroupItem>

{/* Truncate long labels instead of stretching the group */}
<ToggleGroupItem maxWidth="label" value="engineering">
  Engineering and platform
</ToggleGroupItem>

API Reference

The reference lists the source-owned exports and props that are easy to miss in visual examples. Inherited Base UI props remain available unless the wrapper narrows them.

  • ToggleGroup accepts single or multiple selection, controlled or uncontrolled value arrays, orientation, disabled state, Button-style size and Toggle variant.
  • Each ToggleGroupItem requires a unique value and inherits group size and variant unless explicitly overridden.
  • ToggleGroupSeparator defaults to orientation="vertical"; set orientation="horizontal" inside a vertical group.
  • Use multiple for several active items; omit it for one active value.

Motion

Component motion uses the shared Siteplane motion contract. See the global motion guide for provider setup, tokens and reduced-motion behavior.

  • Pressed background, border and focus feedback use the native Toggle motion contract without changing group measurements.
  • Reduced motion keeps selection immediate and removes non-essential feedback transition.

Accessibility

  • Give the group an accessible name and every icon-only item its own aria-label.
  • Keep single- and multi-select semantics aligned with the actual state model.
  • Pair invalid state with visible error text when the group participates in a form.

Implementation Guidance

  • Use Radio Group when explicit form-choice semantics are more important than compact toolbar presentation.
  • Match separator orientation to the group direction.
  • Do not build independent Toggle buttons when their values are meant to share one group state.

On This Page