Skip to content

Toggle

A two-state button that turns a single option on or off and shows its current state.

SourceBase UIRegistry
import { BoldIcon, ItalicIcon, UnderlineIcon } from "lucide-react"

import { Toggle } from "@/components/ui/toggle"

export default function ToggleDemo() {
  return (
    <div className="flex items-center gap-1 rounded-xl border bg-card p-1 shadow-raised">
      <Toggle aria-label="Bold" defaultPressed>
        <BoldIcon />
      </Toggle>
      <Toggle aria-label="Italic">
        <ItalicIcon />
      </Toggle>
      <Toggle aria-label="Underline">
        <UnderlineIcon />
      </Toggle>
    </div>
  )
}

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/toggle.json

Usage

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

Examples

Outline

The outline variant gives each toggle a visible boundary. Use it when toggles stand alone on a page rather than inside a toolbar.

import { EyeOffIcon, LockIcon, PinIcon } from "lucide-react"

import { Toggle } from "@/components/ui/toggle"

export default function ToggleOutline() {
  return (
    <div className="flex items-center gap-2">
      <Toggle variant="outline" aria-label="Pin to sidebar" defaultPressed>
        <PinIcon />
      </Toggle>
      <Toggle variant="outline" aria-label="Lock layer">
        <LockIcon />
      </Toggle>
      <Toggle variant="outline" aria-label="Hide layer">
        <EyeOffIcon />
      </Toggle>
    </div>
  )
}

With text

Pair the icon with a label when the icon alone could be ambiguous, or when the toggle is the only control of its kind on the page.

import { BellIcon, StarIcon } from "lucide-react"

import { Toggle } from "@/components/ui/toggle"

export default function ToggleWithText() {
  return (
    <div className="flex items-center gap-2">
      <Toggle variant="outline" defaultPressed>
        <StarIcon data-icon="inline-start" />
        Starred
      </Toggle>
      <Toggle variant="outline">
        <BellIcon data-icon="inline-start" />
        Watch
      </Toggle>
    </div>
  )
}

Sizes

Toggles share heights with buttons and inputs — sm 32, default 36, lg 40 — so they align in any toolbar.

import { BookmarkIcon } from "lucide-react"

import { Toggle } from "@/components/ui/toggle"

export default function ToggleSizes() {
  return (
    <div className="flex items-center gap-3">
      <Toggle variant="outline" size="sm" aria-label="Bookmark">
        <BookmarkIcon />
      </Toggle>
      <Toggle variant="outline" aria-label="Bookmark">
        <BookmarkIcon />
      </Toggle>
      <Toggle variant="outline" size="lg" aria-label="Bookmark">
        <BookmarkIcon />
      </Toggle>
    </div>
  )
}

Controlled

Control the state with pressed and onPressedChange when something else on the page depends on it. Here the icon and the helper text follow the state.

You'll be notified about new comments.

"use client"

import * as React from "react"
import { BellIcon, BellOffIcon } from "lucide-react"

import { Toggle } from "@/components/ui/toggle"

export default function ToggleControlled() {
  const [subscribed, setSubscribed] = React.useState(true)

  return (
    <div className="flex flex-col items-center gap-3">
      <Toggle
        variant="outline"
        pressed={subscribed}
        onPressedChange={setSubscribed}
      >
        {subscribed ? (
          <BellIcon data-icon="inline-start" />
        ) : (
          <BellOffIcon data-icon="inline-start" />
        )}
        Notifications
      </Toggle>
      <p className="text-sm text-muted-foreground" aria-live="polite">
        {subscribed
          ? "You'll be notified about new comments."
          : "You won't get notifications for this thread."}
      </p>
    </div>
  )
}
const [subscribed, setSubscribed] = React.useState(true)

<Toggle pressed={subscribed} onPressedChange={setSubscribed}>
  {subscribed ? <BellIcon data-icon="inline-start" /> : <BellOffIcon data-icon="inline-start" />}
  Notifications
</Toggle>

Disabled

import { ItalicIcon } from "lucide-react"

import { Toggle } from "@/components/ui/toggle"

export default function ToggleDisabled() {
  return (
    <div className="flex items-center gap-2">
      <Toggle aria-label="Italic" disabled>
        <ItalicIcon />
      </Toggle>
      <Toggle variant="outline" aria-label="Italic" disabled defaultPressed>
        <ItalicIcon />
      </Toggle>
    </div>
  )
}

Guidelines

When to use

  • For formatting and view options that apply immediately: bold, italic, word wrap, grid lines.
  • For marking something: Star, Pin, Watch — where the pressed state is the information.

When not to use

  • For settings that take effect as a preference — use a Switch, which reads as on/off rather than pressed.
  • For one choice among several — use a Toggle Group.
  • To trigger an action that doesn't have a persistent state — use a Button.

Label the option, not the action

A toggle's label names what it controls and stays the same in both states. The pressed styling carries the state; changing the text as well makes people unsure which state they're looking at.

DoThe label names the option; the pressed style shows it's on.
Don’tAn action verb that flips with the state is ambiguous: is it on or off?

Accessibility

  • Renders a native <button> with aria-pressed, so screen readers announce it as a toggle button with its current state.
  • Icon-only toggles need an aria-label. Don't include the state in the label — Bold, not Bold on — because aria-pressed already conveys it.
  • The pressed state is shown with a fill and a darker label, not by color alone; in forced-colors mode it takes the system's selection colors.
KeyBehavior
Tab
Moves focus to the toggle.
SpaceEnter
Toggles the pressed state.

API reference

Toggle

Renders a <button>. Accepts every prop of Base UI's Toggle.

PropTypeDefault
variant

Visual style. Outline adds a border and a raised fill.

"default" | "outline""default"
size

Height and padding, matching buttons and inputs.

"sm" | "default" | "lg""default"
pressed

The controlled pressed state.

booleanNo default
defaultPressed

The initial pressed state when uncontrolled.

booleanfalse
onPressedChange

Called when the pressed state changes.

(pressed: boolean, eventDetails) => voidNo default
disabled

Prevents interaction.

booleanfalse
value

Identifies the toggle inside a Toggle Group.

stringNo default
render

Replaces the rendered element while keeping behavior and styles.

ReactElement | (props, state) => ReactElementNo default

toggleVariants

The class generator, exported so other elements — like Toggle Group items — share the same styles.