caliper uidocs

Toggle and toggle group

Free

A button that stays pressed, alone or in a raised group that holds one choice or many and moves with the arrow keys as a single tab stop.

Installs components/ui/toggle.tsxDepends on @radix-ui/react-slot, lucide-reactMotion none in component source
pnpm dlx shadcn@latest add https://caliperui.site/r/toggle.json

When to use #

Use it for

  • Bold, italic, pin, mute: a setting that is on or off right now.
  • A small set of related options, such as alignment or view.

Skip it when

  • Choosing a page or a mode with a visible result; use tabs or a segmented control.
  • Yes or no in a form; use a switch.

Decisions #

Why it is built this way, so you know what to keep when you change your copy.

Behaviour

One tab stop, arrows move
A group of twelve tools costs one Tab press. Arrow keys follow the reading direction, so it works right to left; in single mode the arrow also selects.

Styling

On is recessed, never raised
A pressed toggle sinks into the shell, the way a toggled tool does in a button group, so it never reads like the selected plate of a segmented control.

Usage

Single always keeps one selected
Single behaves like a radio group. If people need to turn everything off, use multiple.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/toggle.json

Adds its 5 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above. It is built from Toggle, ToggleGroup and ToggleGroupItem.

example.tsx
import { AlignCenter, AlignLeft, AlignRight, Bold, Italic, Pin, Underline } from "lucide-react"
import { useState } from "react"
import { Toggle, ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle"

export function ToggleDemo() {
  const [style, setStyle] = useState(["bold"]);
  const [align, setAlign] = useState(["left"]);
  return (
    <div className="grid justify-items-start gap-6">
      <div className="flex flex-wrap items-center gap-2">
        <Toggle size="icon" aria-label="Bold"><Bold aria-hidden="true" /></Toggle>
        <Toggle defaultPressed><Pin aria-hidden="true" />Pinned</Toggle>
        <Toggle size="icon" aria-label="Underline" disabled><Underline aria-hidden="true" /></Toggle>
      </div>
      <ToggleGroup label="Text style" value={style} onValueChange={setStyle}>
        <ToggleGroupItem value="bold" size="icon" aria-label="Bold"><Bold aria-hidden="true" /></ToggleGroupItem>
        <ToggleGroupItem value="italic" size="icon" aria-label="Italic"><Italic aria-hidden="true" /></ToggleGroupItem>
        <ToggleGroupItem value="underline" size="icon" aria-label="Underline"><Underline aria-hidden="true" /></ToggleGroupItem>
      </ToggleGroup>
      <ToggleGroup type="single" label="Alignment" value={align} onValueChange={setAlign}>
        <ToggleGroupItem value="left"><AlignLeft aria-hidden="true" />Left</ToggleGroupItem>
        <ToggleGroupItem value="center"><AlignCenter aria-hidden="true" />Center</ToggleGroupItem>
        <ToggleGroupItem value="right"><AlignRight aria-hidden="true" />Right</ToggleGroupItem>
      </ToggleGroup>
    </div>
  );
}

API reference #

Toggle

component

Also accepts the native props of its root element and a ref.

PropTypeDefaultDescription
size"sm" | "md" | "lg" | "icon"—
asChildboolean—
loadingboolean—Shows a spinner over the label and swallows clicks while keeping focus and width.
feedbackKeystring—Animate a meaningful label/icon change, such as saving → saved.
pressedboolean—
defaultPressedbooleanfalse
onPressedChange((pressed: boolean) => void)—

ToggleGroup

component

Also accepts the native props of its root element and a ref.

PropTypeDefaultDescription
type"multiple" | "single""multiple"Single behaves like a radio group: one value, always one selected. Multiple lets any combination stay on.
valuestring[]—
defaultValuestring[][]
onValueChange((value: string[]) => void)—
label *string—Names the group for assistive technology, such as "Text style".
disabledboolean—

ToggleGroupItem

component

Also accepts the native props of its root element and a ref.

PropTypeDefaultDescription
size"sm" | "md" | "lg" | "icon"—
asChildboolean—
loadingboolean—Shows a spinner over the label and swallows clicks while keeping focus and width.
feedbackKeystring—Animate a meaningful label/icon change, such as saving → saved.
value *string—

Accessibility #

It is built on native elements and keeps aria-pressed and aria-label in step with state.

KeyBehaviour
↓Moves to the next item or value.
↑Moves to the previous item or value.
HomeJumps to the first item or the minimum.
EndJumps to the last item or the maximum.

Motion #

It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.