Qure UI
Components

Toggle

A button that stays pressed. Not the on/off track — that is Switch.

'use client'import { RulerIcon } from 'lucide-react'import { Toggle } from '@/registry/qure/ui/toggle'export default function ToggleDemo() {  return (    <Toggle defaultPressed aria-label="Show measurements">      <RulerIcon />      Measurements    </Toggle>  )}

Installation

npx shadcn@latest add https://qure-ui.qure.ai/r/toggle.json

Usage

import { Toggle } from '@/components/ui/toggle'
<Toggle defaultPressed aria-label="Show measurements">
  <RulerIcon />
  Measurements
</Toggle>

Toggle is not Switch

This is the one thing to get right on this page, because the design system and the code disagree about the word.

What it isWhat it is forFigma
SwitchAn on/off track with a knobA setting that is on or off, and applies as soon as it changes"Toggle", node 3524:4591
ToggleA button that stays visibly pressedA mode, usually one of several, applied to what is on screen right nownone — derived from Button

Figma's component named "Toggle" is the track, and it is our Switch. Base UI's toggle is the pressed button, and it is this. The names were kept as Base UI has them rather than as Figma has them, because a codebase where Toggle means something different from every other React library costs more than a design system where one component has two names.

The test that decides it: read the control aloud with "on" and "off" after it. "Notify on critical findings — on" is a Switch. "Measurements — on" is not how anyone says it; that is a Toggle.

A toggle is aria-pressed, not role="switch", so a screen reader announces it as a pressed button and does not promise the setting has been saved anywhere.

Variants

'use client'import { CrosshairIcon, RulerIcon } from 'lucide-react'import { Toggle } from '@/registry/qure/ui/toggle'export default function ToggleVariant() {  return (    <>      <Toggle variant="default" defaultPressed>        <RulerIcon />        Measurements      </Toggle>      <Toggle variant="outline">        <CrosshairIcon />        Crosshairs      </Toggle>      <Toggle variant="outline" iconOnly aria-label="Localiser lines">        <CrosshairIcon />      </Toggle>    </>  )}

default is borderless and belongs on a toolbar or inside a Toggle Group, where the buttons beside it give it an edge. outline keeps a boundary and is the right choice for a toggle standing alone on a plain surface — an unpressed borderless button on an empty background does not read as a control at all.

iconOnly squares the button off, so a row of icon toggles is a row of squares rather than of slightly-too-wide rectangles. It needs an aria-label.

Size

'use client'import { LayersIcon } from 'lucide-react'import { Toggle } from '@/registry/qure/ui/toggle'export default function ToggleSize() {  return (    <>      <Toggle size="sm" variant="outline" defaultPressed>        <LayersIcon />        Overlay      </Toggle>      <Toggle size="md" variant="outline" defaultPressed>        <LayersIcon />        Overlay      </Toggle>      <Toggle size="lg" variant="outline" defaultPressed>        <LayersIcon />        Overlay      </Toggle>    </>  )}

32, 40 and 48, matching Button and the field scale, so a toggle sits on a row with either.

Controlled

'use client'import * as React from 'react'import { ContrastIcon } from 'lucide-react'import { Toggle } from '@/registry/qure/ui/toggle'export default function ToggleControlled() {  const [inverted, setInverted] = React.useState(false)  return (    <div className="flex flex-col items-center gap-4">      <Toggle variant="outline" pressed={inverted} onPressedChange={setInverted}>        <ContrastIcon />        Invert greyscale      </Toggle>      <div        data-inverted={inverted || undefined}        className="h-16 w-40 rounded-md bg-linear-to-r from-black to-white transition-[filter] data-[inverted]:invert"      />      <Toggle disabled>Unavailable on this series</Toggle>    </div>  )}

pressed with onPressedChange when the state lives somewhere else — a viewer's rendering options, a URL parameter. defaultPressed otherwise. The disabled toggle stays in the tab order and announces itself as disabled, rather than vanishing.

API Reference

Everything Base UI's Toggle accepts, plus:

Prop

Type

And from Base UI:

Prop

Type

On this page