Switch
A control that toggles between checked and unchecked states.
npx shadcn@latest add @velaui/switchpnpm dlx velaui add @velaui/switchyarn dlx shadcn@latest add @velaui/switchbunx --bun shadcn@latest add @velaui/switchveni add switchInstalls the copy of vela-ui that veni-builder serves to Venizia apps; veni-builder serves the Radix UI copy only, so this tab exists for that base alone.
npx shadcn@latest add @velaui/switchpnpm dlx velaui add @velaui/switchyarn dlx shadcn@latest add @velaui/switchbunx --bun shadcn@latest add @velaui/switchExamples
- Default
- Checked
- With label
- Disabled
- Invalid
Sizes
Usage
Props
Switch
| Prop | Type | Default |
|---|---|---|
size | "sm" | "md" | "lg" | "md" |
Takes these and every prop of SwitchProps.
SwitchField
Takes every prop of SwitchFieldProps.
Dependencies
- npm
class-variance-authority@0.7.1radix-ui@1.6.0- Installs with it
- nothing else
- npm
@base-ui/react@1.6.0class-variance-authority@0.7.1- Installs with it
- nothing else
Source
components/ui/switch.tsx
/* eslint-disable react-refresh/only-export-components */
import * as React from "react";
import { Switch as SwitchPrimitive } from "radix-ui";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const switchVariants = cva(
// The 2px transparent border IS the design's 2px inset — focus-unchecked and
// invalid both draw a real border there, so reserving it keeps the thumb from
// shifting when either state turns on.
"peer group/switch relative inline-flex shrink-0 items-center rounded-full border-2 border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 data-unchecked:bg-bg-surface-2 data-checked:bg-bg-foreground data-unchecked:focus-visible:ring-0 data-checked:focus-visible:ring-2 data-checked:focus-visible:ring-[#737373bf] data-unchecked:focus-visible:border-border-default data-unchecked:focus-visible:bg-bg-surface-1 data-unchecked:aria-invalid:border-negative-middle data-unchecked:aria-invalid:bg-negative data-checked:aria-invalid:border-negative-middle data-checked:aria-invalid:bg-negative aria-invalid:focus-visible:ring-negative-middle/25 disabled:cursor-not-allowed data-unchecked:disabled:bg-bg-disabled data-checked:disabled:bg-bg-disabled",
{
variants: {
size: {
sm: "h-4 w-7",
md: "h-[18px] w-8",
lg: "h-[22px] w-10",
},
},
defaultVariants: {
size: "md",
},
},
);
export type SwitchProps = React.ComponentProps<typeof SwitchPrimitive.Root> &
VariantProps<typeof switchVariants>;
function Switch({ className, size = "md", ...props }: SwitchProps) {
return (
<SwitchPrimitive.Root
data-slot="switch"
data-size={size}
className={cn(switchVariants({ size }), className)}
{...props}
>
<SwitchPrimitive.Thumb
data-slot="switch-thumb"
className="pointer-events-none block rounded-full bg-bg-main ring-0 transition-transform group-data-[size=sm]/switch:size-3 group-data-[size=md]/switch:size-3.5 group-data-[size=lg]/switch:size-[18px] group-aria-invalid/switch:bg-negative-middle data-unchecked:translate-x-0 data-checked:translate-x-full group-disabled/switch:bg-bg-surface-2"
/>
</SwitchPrimitive.Root>
);
}
export type SwitchFieldProps = React.ComponentProps<"div">;
/** Switch paired with its label — the "Switch.Field" part of the design. */
function SwitchField({ className, ...props }: SwitchFieldProps) {
return (
<div
data-slot="switch-field"
className={cn(
"inline-flex items-center gap-2 text-sm leading-[1.4] font-medium text-text-strong",
className,
)}
{...props}
/>
);
}
export { Switch, SwitchField, switchVariants };components/ui/switch.tsx
/* eslint-disable react-refresh/only-export-components */
import * as React from "react";
import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const switchVariants = cva(
// The 2px transparent border IS the design's 2px inset — focus-unchecked and
// invalid both draw a real border there, so reserving it keeps the thumb from
// shifting when either state turns on. Base UI renders a span (not a button),
// so disabled styling keys off data-disabled instead of :disabled.
"peer group/switch relative inline-flex shrink-0 items-center rounded-full border-2 border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 data-unchecked:bg-bg-surface-2 data-checked:bg-bg-foreground data-unchecked:focus-visible:ring-0 data-checked:focus-visible:ring-2 data-checked:focus-visible:ring-[#737373bf] data-unchecked:focus-visible:border-border-default data-unchecked:focus-visible:bg-bg-surface-1 data-unchecked:aria-invalid:border-negative-middle data-unchecked:aria-invalid:bg-negative data-checked:aria-invalid:border-negative-middle data-checked:aria-invalid:bg-negative aria-invalid:focus-visible:ring-negative-middle/25 data-disabled:cursor-not-allowed data-unchecked:data-disabled:bg-bg-disabled data-checked:data-disabled:bg-bg-disabled",
{
variants: {
size: {
sm: "h-4 w-7",
md: "h-[18px] w-8",
lg: "h-[22px] w-10",
},
},
defaultVariants: {
size: "md",
},
},
);
export type SwitchProps = SwitchPrimitive.Root.Props &
VariantProps<typeof switchVariants>;
function Switch({ className, size = "md", ...props }: SwitchProps) {
return (
<SwitchPrimitive.Root
data-slot="switch"
data-size={size}
className={cn(switchVariants({ size }), className)}
{...props}
>
<SwitchPrimitive.Thumb
data-slot="switch-thumb"
className="pointer-events-none block rounded-full bg-bg-main ring-0 transition-transform group-data-[size=sm]/switch:size-3 group-data-[size=md]/switch:size-3.5 group-data-[size=lg]/switch:size-[18px] group-aria-invalid/switch:bg-negative-middle data-unchecked:translate-x-0 data-checked:translate-x-full group-data-disabled/switch:bg-bg-surface-2"
/>
</SwitchPrimitive.Root>
);
}
export type SwitchFieldProps = React.ComponentProps<"div">;
/** Switch paired with its label — the "Switch.Field" part of the design. */
function SwitchField({ className, ...props }: SwitchFieldProps) {
return (
<div
data-slot="switch-field"
className={cn(
"inline-flex items-center gap-2 text-sm leading-[1.4] font-medium text-text-strong",
className,
)}
{...props}
/>
);
}
export { Switch, SwitchField, switchVariants };