v0.5

Switch Group

Labelled switch row that pairs a single Switch with a label, description, and footer.

Get alerts the moment something happens on your account.

Description

SwitchGroup is a horizontal form-field row: one Switch, a label/description block, and an optional footer for helper text. It wraps the in-house Field (which provides the labelled layout) and cascades variant, size, shape, thumbType, and ioTrigger to the child Switch through SwitchContext, so you configure the look once on the group instead of on every row.

Reach for it when a live setting needs a real label: notification preferences, feature flags, privacy toggles. The switch commits the change the moment it flips, so the row reads as "this is on now," not "this will apply when I submit." Stack several rows in a column for a settings panel and the cascade keeps every switch visually aligned.

Don't use it inside a form that only saves on an explicit submit; reach for Checkbox or CheckboxGroup there. Don't use it for one-of-many selection; that's RadioGroup. For a bare toggle in a toolbar or table cell with no label, drop down to Switch directly and skip the Field layout.

Installation

pnpm dlx @create-ui/cli add switch-group

Anatomy

SwitchGroup composes Field around one Switch and its label content. The label block lives inside FieldContent so the description and footer stay aligned with the switch's first line.

<SwitchGroup>
  <Switch id="…" />
  <FieldContent>
    <LabelMain>
      <Label htmlFor="…" />
      <LabelDescription />
    </LabelMain>
    <FieldFooter />
  </FieldContent>
</SwitchGroup>

Usage

import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
import { SwitchGroup } from "@/components/ui/switch-group"
<SwitchGroup>
  <Switch id="alerts" defaultChecked />
  <Label htmlFor="alerts">Email alerts</Label>
</SwitchGroup>

Examples

Variants

variant sets the track color and cascades to the child Switch. Five options: primary (default), info, neutral, inverse for dark surfaces, and semantic (red when off, green when on).

The default product intent.

Blue track for informational toggles.

Quiet grey track for low-emphasis rows.

For use on dark surfaces.

Green when on, red when off.

Sizes

size scales the switch and the label together through Field and SwitchContext. Pick xs for dense settings tables and md when the row stands alone.

Switch and label scale together from the group.

Switch and label scale together from the group.

Switch and label scale together from the group.

Placement

placement="left" (default) puts the switch before the label. placement="right" flips the row direction without changing DOM order, which is useful when aligning switches against a right-hand edge.

Default. Switch before the label.

Mirror layout. Switch after the label.

Thumb Type

thumbType cascades to the child Switch. short (default) is a compact knob; long widens the thumb and extends its travel for a more tactile feel.

Default. Compact square knob.

Wider knob with a longer travel distance.

I/O Trigger

ioTrigger cascades to the child Switch, rendering a pipe glyph when on and a circle when off inside the track. Use it when color alone should not carry the state.

Shows an I/O glyph inside the track so the state reads beyond color.

Thumb Icon

thumbIcon on the child Switch shows a check icon on the thumb when on and a close icon when off. Set it per switch; unlike ioTrigger, it does not cascade from the group.

A check icon rides the thumb while the setting is on.

A close icon takes its place once the setting is off.

Disabled

disabled on the group sets the Field context's disabled flag, which fades the label and description and blocks pointer events on the row. Pass disabled on the child Switch too so it leaves the tab order.

Disabled state cascades to label and description.

The unchecked row reads muted too.

Accessibility

SwitchGroup delegates all focus and ARIA behavior to its child Switch; the wrapping Field is a role="group" div, not a control. Keyboard interaction lives on the switch.

KeyDescription
SpaceToggles the focused child switch.
TabMoves focus to the next focusable element.

ARIA notes:

  • The root <div> is role="group" and exposes data-invalid, data-disabled, and data-loading so the label, description, and footer restyle through Tailwind group selectors.
  • Always pair the child Switch id with the Label htmlFor so screen readers announce what the toggle controls.
  • The glyphs rendered by ioTrigger (and any thumb icons) are aria-hidden="true" and do not affect the accessible name.

Styling

Tailwind override: pass className to merge Tailwind classes on the root Field row:

<SwitchGroup className="w-[340px]" />

Data slots and attributes: the component sets these for CSS targeting:

  • data-slot="switch-group" on the root.
  • data-variant="<variant>", data-placement="<placement>", data-shape="<shape>", data-thumb-type="<thumbType>" on the root.
  • Inherited from Field: data-slot="field", data-size, data-orientation="horizontal", data-invalid, data-disabled, data-loading.

Target the right-aligned layout in CSS:

[data-slot="switch-group"][data-placement="right"] {
  flex-direction: row-reverse;
}
  • Switch: the bare toggle without label or footer; use it directly in toolbars and table cells.
  • CheckboxGroup: use when the row belongs to a form that saves on submit, or needs an indeterminate state.
  • RadioGroup: single-select alternative for picking one option from a set.
  • Field: the lower-level form-field wrapper this component composes; reach for it when you need a different layout.

API Reference

SwitchGroup

A horizontal Field that wraps one Switch plus its label content and cascades switch styling through context. Extends React.ComponentProps<typeof Field> minus the orientation and size props (which are managed internally), so other Field props (invalid, loading, disabled, …) pass through.

Props

PropTypeDefaultDescription
disabledboolean-Disables interaction and cascades a disabled style to label and description.
invalidboolean-Marks the row as invalid; cascades aria-invalid styling through Field.
loadingboolean-Forwarded to Field; useful for skeleton/spinner states.
ioTriggerbooleanfalseCascades an I/O glyph onto the child switch's track.
childrenReact.ReactNode-The Switch plus its label content (typically inside FieldContent).
classNamestring-Tailwind classes merged with the component's CVA classes on the root.

Variants

VariantOptionsDefaultDescription
variant"primary" "info" "neutral" "inverse" "semantic""primary"Track color cascaded to the child switch. semantic is red off, green on.
size"xs" "sm" "md""md"Cascades to the child switch and label via context.
shape"pill" "rounded""pill"Cascades to the child switch. pill is fully rounded.
thumbType"short" "long""short"Cascades to the child switch. long widens the thumb and its travel.
placement"left" "right""left"Row direction. "right" flips the visual order while preserving DOM order.