Skip to content

Toggle

Stays pressed until it's pressed again, like Bold in a text editor.

Library
What’s this?

Installation

pnpm dlx shadcn@latest add @wisty/toggle

The CLI installs the Radix or Base UI file to match your project.

If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.

When to use

Use it

  • For a button that stays pressed until it's pressed again, like Bold in a text editor.
  • For an icon button that shows or hides part of a view, like a list view's charts or filters, beside buttons that don't stay pressed.
  • For a labelled button that marks something, like Favorite.
  • In place of Lightning's stateful button icon, or a stateful button whose label stays the same.

Don't use it

  • A setting turns on or off in a form or settings list. Use Switch or Checkbox.
  • People pick one of a few related options, like text alignment. Use Toggle Group.
  • The button runs an action and doesn't stay pressed. Use Button.
  • The label changes to name the next action, such as Follow becoming Unfollow. Use Button.

Usage

An icon-only toggle takes an aria-label, which also gives it the square icon padding. A toggle with text is named by its text and pads like a button, as the Text example shows.

import { BoldIcon } from "lucide-react"

import { Toggle } from "@/components/ui/toggle"
<Toggle aria-label="Bold" variant="outline">
  <BoldIcon />
</Toggle>

Examples

Bare

Icon toggles with no border, the `default` variant, as in a note's formatting toolbar, Bold pressed.

Text

Toggles with labels: one off, one pressed, and a bare text toggle.

Sizes

Small, default and large, set with `size` on `Toggle`.

Disabled

Disabled toggles, pressed and not, in both variants.

API reference

Toggle

PropTypeDefault
variant"default" | "outline""default"
size"default" | "sm" | "lg""default"

Also takes every Radix Toggle.Root (opens in a new tab) prop.

SLDS mapping

Toggle is SLDS's stateful icon button, and a toggle with text is its dual-stateful button (slds-button_dual-stateful).

Known gaps:

  • A bare (variant="default") toggle with text pads like a button. SLDS's base stateful button has no padding; ours keeps it so the pressed fill has room.
PropValueSLDSNotes
variantdefaultslds-button_icon-containerBare. Pressed is slds-is-selected: a brand fill.
variantoutlineslds-button_icon-border-filledBordered. Pressed is slds-is-selected. With text it's slds-button_neutral slds-button_dual-stateful.
sizedefaultslds-button_icon-containerButton's default height.
sizesmnoneButton's small height. SLDS's small icon button is smaller.
sizelgnoneButton's large height.

Accessibility

  • Name an icon-only toggle with aria-label. Keep both the visible label and the name the same when it's pressed; aria-pressed carries the state.
  • A disabled toggle can't take focus or show a tooltip. Say why in nearby text.

Motion

  • In Cosmos, hovering lifts an outline toggle 1.5px and a pressed toggle 2px, each with an edge shadow, over 150ms. Lightning Blue sets the lift and the shadow to none.
  • The lift runs with reduced motion too.

Responsive behavior

  • It keeps its height and doesn't shrink in a flex row, and text doesn't wrap. Let a toolbar wrap with flex-wrap.

Rules

  • Don't add aria-label to a toggle with visible text: it gets icon padding, and the label replaces the text as the name.
  • Show the pressed state with defaultPressed or pressed, never with a class. The state has to reach aria-pressed.
  • Never override colors, radius or height with utility classes. The tokens carry each theme.