Skip to content
Primitives

Toggle

Sharp two-state button that fills signal when pressed; `default` (borderless) and `outline` (keylined) variants.

● LIVE
npx shadcn@latest add https://okibi.cndr.dev/r/toggle.json

Installation

Install the theme first, then add the component:

npx shadcn@latest add https://okibi.cndr.dev/r/toggle.json

Install the required dependencies:

npm install @radix-ui/react-toggle class-variance-authority

Copy the source from the Code tab above into components/ui/toggle.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.

Usage

import { BoldIcon } from "lucide-react"

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

<Toggle variant="outline" aria-label="Toggle bold">
  <BoldIcon />
</Toggle>

Examples

Outline

The outline variant adds a keyline that snaps to signal on hover; pair the icon with a text label.

● LIVE

Pressed

Set pressed/defaultPressed to show the signal-fill on-state statically, in both default and outline variants.

● LIVE

API Reference

Toggle

PropTypeDefaultDescription
variant"default" | "outline""default"Borderless or keylined.
size"sm" | "md" | "lg""md"Control size.
pressedbooleanControlled pressed state.
defaultPressedbooleanfalseInitial pressed state (uncontrolled).
onPressedChange(pressed: boolean) => voidFires when toggled.
disabledbooleanfalseLock the toggle in its current state.

Accessibility

  • Built on Radix Toggle.Root, so pressed state is exposed as aria-pressed automatically.
  • Icon-only toggles have no text — they require an aria-label describing the action they control.
  • Focus shows a 2px signal ring with offset; disabled sets disabled:pointer-events-none and dims to opacity-60.
  • The pressed look is the signal primary fill, an AA-enforced color pair — state isn't carried by a subtle tint alone.

Guidelines

Do

  • Use it for a single on/off control whose state persists, like a formatting button.
  • Give icon-only toggles an aria-label, and prefer the outline variant when the control sits on a busy surface.
  • Drive it with pressed / onPressedChange when the state lives in your own model.

Don't

  • Don't use it for an action that fires and forgets — that's a Button, not a stateful toggle.
  • Don't group several mutually exclusive toggles by hand; reach for ToggleGroup so roving focus and selection are handled.

On this page