# Toggle

> A React toggle button that stays pressed, for bold and italic in an editor, a favorite heart, mute, or a filter pill.

- Type: React component (Buttons & Actions)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add toggle`
- Docs: https://wingo-ui.com/components/toggle

## Interaction

Click or tap it and it stays pressed: the surface takes the selected tint (soft turns into a solid fill) and it dips on a quick spring while your finger is down. Click again to release it. With a mouse it tints on hover, deeper when it is already pressed. Tab to it and press Space or Enter; screen readers hear the state through aria-pressed while the label stays the same. With pressedIcon the icon pops into its pressed form (an outline heart turns into a filled one, a bell into a muted bell), and a filter pill grows a check mark. A shortcut such as mod+b flips it from anywhere on the page. On a phone the medium size is 44px tall and the small one keeps a 44px hit area.

## Installation

```bash
npx wingo-ui@latest add toggle
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/toggle` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/toggle.tsx`

npm dependencies: `npm i @radix-ui/react-toggle @radix-ui/react-slot motion`

Wingo UI dependencies (installed automatically):

- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [useShortcut](https://wingo-ui.com/components/use-shortcut) ([Markdown](https://wingo-ui.com/components/use-shortcut.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { Check, Heart } from "lucide-react"
import { Toggle } from "@/components/ui/toggle"

export function ListingActions({ saved, onSavedChange }: { saved: boolean; onSavedChange: (saved: boolean) => void }) {
  return (
    <div className="flex items-center gap-2">
      <Toggle variant="outline" radius="full" pressedIcon={<Check />}>
        Delivery only
      </Toggle>
      <Toggle
        size="icon"
        radius="full"
        tone="danger"
        pressed={saved}
        onPressedChange={onSavedChange}
        pressedIcon={<Heart className="fill-current" />}
        aria-label="Save to favorites"
      >
        <Heart />
      </Toggle>
    </div>
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `children?` | `ReactNode` | `"Delivery only"` | The label. For icon sizes pass the icon itself plus an aria-label, and keep the label the same in both states and name what pressed means (Bold, Mute notifications): aria-pressed already says whether it is on. |
| `variant?` | `"ghost" \| "outline" \| "soft"` | `"ghost"` | ghost is transparent with muted text and takes the selected tint when pressed (toolbars). outline adds a hairline border that turns to the tone when pressed (filter chips). soft rests on a light fill and becomes the solid tone fill when pressed, so the on state is loud. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Color of the pressed state. neutral is the gray selected tint (black or white fill on soft); the other tones tint the pressed state and color its text. |
| `color?` | `string` | `""` | Any CSS color for the pressed state. Replaces the tone; on soft the text on the fill turns black or white by lightness. Leave empty to use the tone. |
| `size?` | `"sm" \| "md" \| "lg" \| "icon-sm" \| "icon" \| "icon-lg"` | `"md"` | The button's size table: 32 / 40 / 48px tall with matching padding, type and icon size. The icon sizes are square and need an aria-label. On touch screens md grows to 44px and sm keeps its look with a 44px hit area. |
| `radius?` | `"none" \| "sm" \| "md" \| "lg" \| "full"` | `"md"` | Corner shape, scaled with the size (md is 8 / 10 / 12px). full makes a pill, the usual look for filters. |
| `defaultPressed?` | `boolean` | `false` | Starting state while uncontrolled. |
| `pressed?` | `boolean` |  | Controlled state. Pair it with onPressedChange; without a handler the toggle stays where you put it. |
| `onPressedChange?` | `(pressed: boolean) => void` |  | Called with the next state on click, Space, Enter or the shortcut. |
| `disabled?` | `boolean` | `false` | Dims the toggle, blocks clicks and the shortcut, and keeps its current state. |
| `fullWidth?` | `boolean` | `false` | Stretches to the container width, e.g. a Follow toggle at the bottom of a phone screen. |
| `shortcut?` | `string` | `""` | A key combo in mod+k notation ("mod+b", "shift+mod+x"; mod is ⌘ on Apple devices and Ctrl elsewhere) that flips the toggle from anywhere on the page. Also sets aria-keyshortcuts. Bare keys are ignored while typing. Pass it only while the toggle should listen, e.g. while its editor has focus. |
| `asChild?` | `boolean` | `false` | Renders your single child (a router link for URL filters, your own button) with the toggle's look, state, press and icons. A child that is not a button gets role="button" and flips on Space too, so aria-pressed is announced. The demo turns the first filter into an <a>. As in Radix, calling event.preventDefault() in your onClick cancels the flip. |
| `leftIcon?` | `ReactNode` |  | An icon before the label. Unsized svgs follow the size (14 / 16 / 18px). |
| `pressedIcon?` | `ReactNode` |  | Replaces leftIcon (or the icon child in icon sizes) while pressed, popping in on a spring: an outline heart to a filled one, Bell to BellOff. On a text toggle without leftIcon it only shows while pressed and the width springs open, like a filter chip growing a check, with the neighbors gliding along. |
| `classNames?` | `Partial<Record<"icon" \| "label", string>>` |  | Extra classes for the inner parts: the icon wrapper and the label wrapper. |
| `className?` | `string` |  | Extra classes for the toggle itself, merged last. Style the pressed state with data-[state=on]:, e.g. data-[state=on]:bg-background/90. |

## Examples

- **Editor formatting**: Icon toggles in a toolbar. Focus the text and ⌘B, ⌘I, ⌘U and ⇧⌘X flip them through the shortcut prop.
- **Variants and states**: ghost and outline take the selected tint when pressed; soft turns into the solid fill.
- **Tones**: The tone only colors the pressed state: a solid fill on soft, a tint and border on outline.
- **Sizes**: 32, 40 and 48px plus square icon sizes; 44px on touch screens.
- **Filter pills**: radius full with a check as pressedIcon: the pill springs open to make room and its neighbors glide along.
- **Mute notifications**: Bell turns into BellOff. The aria-label names what pressed means (Mute notifications) and stays the same; aria-pressed carries the state.
- **Custom color, full width**: Any CSS color for the pressed fill, fullWidth for the bottom of a phone screen.

## See also

- [Toggle Group](https://wingo-ui.com/components/toggle-group) ([Markdown](https://wingo-ui.com/components/toggle-group.md))
- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Favorite button](https://wingo-ui.com/components/favorite-button) ([Markdown](https://wingo-ui.com/components/favorite-button.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [useShortcut](https://wingo-ui.com/components/use-shortcut) ([Markdown](https://wingo-ui.com/components/use-shortcut.md))

---

Canonical: https://wingo-ui.com/components/toggle
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
