On/off switch with an accent track and sprung thumb.
Email notifications
Weekly product digest.
Reduced motion
Tone animations down.
Disabled
npx @agents-npm-packages/vibeui add switch --dir ./src/components/uiImport
import { Switch } from "@/components/ui/switch"Generated from the component's TypeScript types, so it always matches the source. * marks a required prop.
| Prop | Type | Default | Description |
|---|---|---|---|
| checked | boolean | — | Controlled checked state. |
| defaultChecked | boolean | — | Initial checked state when uncontrolled. |
| onCheckedChange | ((checked: boolean) => void) | — | Called with the new checked state. |
| disabled | boolean | — | Prevents interaction and dims the control. |
| className | string | — | Extra classes, merged with the component's own. |
| id | string | — | DOM id. |
| aria-label | string | — | Accessible name, required when there is no visible label. |
"use client";
import * as React from "react";
import { cn } from "@/lib/utils";
/** On/off switch with an accent track and sprung thumb. */
export function Switch({
checked,
defaultChecked,
onCheckedChange,
disabled,
className,
id,
"aria-label": ariaLabel,
}: {
checked?: boolean;
defaultChecked?: boolean;
onCheckedChange?: (checked: boolean) => void;
disabled?: boolean;
className?: string;
id?: string;
"aria-label"?: string;
}) {
const [uncontrolled, setUncontrolled] = React.useState(!!defaultChecked);
const isOn = checked ?? uncontrolled;
return (
<button
type="button"
role="switch"
id={id}
aria-checked={isOn}
aria-label={ariaLabel}
disabled={disabled}
onClick={() => {
const next = !isOn;
setUncontrolled(next);
onCheckedChange?.(next);
}}
className={cn(
"group relative inline-flex h-6 w-[42px] shrink-0 items-center rounded-full border p-0.5 transition-colors duration-200 ease-out disabled:pointer-events-none disabled:opacity-45",
isOn
? "border-transparent bg-accent"
: "border-line-strong bg-surface-sunken hover:border-faint",
className,
)}
>
<span
className={cn(
"pointer-events-none block h-5 w-5 rounded-full shadow-[0_1px_3px_rgb(0_0_0/0.3)] transition-[transform,width,background-color] duration-300 ease-spring group-active:w-[22px]",
isOn ? "bg-accent-ink" : "bg-muted",
isOn ? "translate-x-[18px] group-active:translate-x-4" : "translate-x-0",
)}
/>
</button>
);
}
Exports Switch. Peer dependencies: clsx, tailwind-merge.