Primitives
Switch
Sharp toggle switch; signal fill when on.
● LIVE
"use client";import { Label } from "@/registry/okibi/ui/label";import { Switch } from "@/registry/okibi/ui/switch";export default function SwitchDemo() { return ( <div className="flex items-center gap-2"> <Switch id="switch-demo-firewall" defaultChecked /> <Label htmlFor="switch-demo-firewall">Firewall</Label> </div> );}npx shadcn@latest add https://okibi.cndr.dev/r/switch.jsonInstallation
Install the theme first, then add the component:
npx shadcn@latest add https://okibi.cndr.dev/r/switch.jsonInstall the required dependencies:
npm install @radix-ui/react-switchCopy the source from the Code tab above into components/ui/switch.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.
Usage
import { Switch } from "@/components/ui/switch"
<Switch id="ice" defaultChecked />Anatomy
<Label htmlFor="ice">Ice protection</Label>
<Switch id="ice" defaultChecked /> {/* single Radix Switch.Root + Thumb */}Examples
Disabled
The native disabled attribute locks the toggle in its current on/off state.
● LIVE
"use client";import { Label } from "@/registry/okibi/ui/label";import { Switch } from "@/registry/okibi/ui/switch";export default function SwitchDisabledDemo() { return ( <div className="space-y-3"> <div className="flex items-center gap-2"> <Switch id="switch-disabled-off" disabled /> <Label htmlFor="switch-disabled-off">Relay offline</Label> </div> <div className="flex items-center gap-2"> <Switch id="switch-disabled-on" disabled defaultChecked /> <Label htmlFor="switch-disabled-on">Firewall locked</Label> </div> </div> );}API Reference
Switch
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | – | Controlled on/off state. |
defaultChecked | boolean | false | Initial state when uncontrolled. |
onCheckedChange | (checked: boolean) => void | – | Fires when the switch is toggled. |
disabled | boolean | false | Lock the switch and drop it to opacity-60. |
required | boolean | false | Mark the control required inside a form. |
name | string | – | Field name submitted with the surrounding form. |
Accessibility
- Built on Radix
Switch.Root, exposingrole='switch'witharia-checkedtoggled by Space / Enter. - On / off is reflected via
data-statedriving the track color and the thumb'stranslate-x. aria-invalid='true'flips the track to the destructive keyline (border-destructive,ring-destructive).- Focus shows the 2px signal ring (
focus-visible:ring-2 ring-ring ring-offset-2). - The control is 20x36px — below the 44px touch minimum — so always pair it with a clickable
Labelto extend the target. - Disabled is
disabled:opacity-60with pointer events off; the thumb slide uses the fast token, no spring.
Guidelines
Do
- Use a switch for an instant on/off setting that takes effect immediately, with no Save step.
- Always pair with a
Label(viahtmlFor/id) to name the control and widen the tap target. - Drive it controlled with
checked+onCheckedChangewhen the value lives in app state.
Don't
- Don't use a switch where the change only applies after submitting a form — use a
Checkbox. - Don't leave the switch unlabeled; an icon alone is not an accessible name.
- Don't stack many switches without labels in a tight row — they fall below the touch target.