Use a switch for settings that apply as soon as they are turned on or off.
tsx
import { Switch } from "@/components/ui/switch"export function Example() { return ( <label className="flex items-center gap-2.5 text-sm tracking-wide"> Airplane mode <Switch id="airplane" /> </label> )}Installation
npx @arctis-sh/@arctis-sh/ui@latest add switchUsage
import { Switch } from "@/components/ui/switch"<Switch /><label className="flex items-center gap-2.5 text-sm tracking-wide"> Airplane mode <Switch id="airplane" /></label>Description
Pair the control with a title and short helper text, aligned on the trailing edge.
tsx
import { Switch } from "@/components/ui/switch"export function Example() { return ( <label className="flex w-full items-start justify-between gap-4"> <span className="grid gap-1"> <span className="text-sm font-medium tracking-wide"> Watch theme tokens </span> <span className="text-sm text-muted-foreground"> Rebuild docs when CSS variables change under globals.css. </span> </span> <Switch id="watch-tokens" className="mt-0.5" /> </label> )}Choice card
Wrap the row in a Card to make the full surface clickable. The unchecked card stays outlined; the checked card fills.
tsx
import * as React from "react"import { Card, CardContent } from "@/components/ui/card"import { Switch } from "@/components/ui/switch"import { cn } from "@/lib/utils"export function Example() { const [docs, setDocs] = React.useState(true) const [registry, setRegistry] = React.useState(false) return ( <div className="grid w-full gap-3"> <label className="block w-full cursor-pointer"> <Card className={cn("py-0", !docs && "bg-transparent")}> <CardContent className="flex items-start justify-between gap-4 py-3"> <span className="grid gap-1"> <span className="text-sm font-medium tracking-wide"> Include in docs build </span> <span className="text-sm text-muted-foreground"> Ships markdown, demos, and tokens with the registry export. </span> </span> <Switch id="card-docs" checked={docs} onCheckedChange={setDocs} className="mt-0.5" /> </CardContent> </Card> </label> <label className="block w-full cursor-pointer"> <Card className={cn("py-0", !registry && "bg-transparent")}> <CardContent className="flex items-start justify-between gap-4 py-3"> <span className="grid gap-1"> <span className="text-sm font-medium tracking-wide"> Sync local registry </span> <span className="text-sm text-muted-foreground"> Pull component updates when the registry changes. </span> </span> <Switch id="card-registry" checked={registry} onCheckedChange={setRegistry} className="mt-0.5" /> </CardContent> </Card> </label> </div> )}Disabled
Set disabled to lock the control. Dim the label too so the whole row reads as inactive.
tsx
import { Switch } from "@/components/ui/switch"export function Example() { return ( <label className="flex items-center gap-2.5 text-sm tracking-wide opacity-60"> Publish package — unlock after review <Switch id="publish-locked" disabled /> </label> )}Invalid
Mark the control with aria-invalid when validation fails, and use destructive text for the label.
tsx
import { Switch } from "@/components/ui/switch"export function Example() { return ( <label className="flex items-center gap-2.5 text-sm font-medium tracking-wide text-destructive"> Accept the license <Switch id="license" aria-invalid /> </label> )}Size
Use size="sm" for a tighter track. Default is the standard size.
tsx
import { Switch } from "@/components/ui/switch"export function Example() { return ( <div className="flex w-44 flex-col gap-4"> <label className="flex w-full items-center justify-between gap-4 text-sm tracking-wide"> Small <Switch id="size-sm" size="sm" defaultChecked /> </label> <label className="flex w-full items-center justify-between gap-4 text-sm tracking-wide"> Default <Switch id="size-default" defaultChecked /> </label> </div> )}Radius
Use radius for square (none), standard (default), or pill-shaped (full) corners.
tsx
import { Switch } from "@/components/ui/switch"export function Example() { return ( <div className="flex w-52 flex-col gap-4"> <label className="flex w-full items-center justify-between gap-4 text-sm tracking-wide"> None <Switch id="radius-none" radius="none" defaultChecked /> </label> <label className="flex w-full items-center justify-between gap-4 text-sm tracking-wide"> Usual corners <Switch id="radius-default" defaultChecked /> </label> <label className="flex w-full items-center justify-between gap-4 text-sm tracking-wide"> Full <Switch id="radius-full" radius="full" defaultChecked /> </label> </div> )}API Reference
Switch
| Prop | Type | Default |
|---|---|---|
| checked | boolean | — |
| defaultChecked | boolean | false |
| onCheckedChange | (checked: boolean) => void | — |
| size | "default" | "sm" | "default" |
| radius | "none" | "default" | "full" | "default" |
| disabled | boolean | false |
| aria-invalid | boolean | — |
| id | string | — |
| className | string | — |
Also accepts the other native button attributes.
<Switch checked={checked} onCheckedChange={setChecked} />