A control that indicates whether a setting is on or off.
Installation
npx shadcn@latest add @caprice/switchnpm install @caprice-ui/reactInstall the following dependencies:
npm install @base-ui/react lucide-reactAdd a cn helper
import { defineConfig, type VariantProps } from 'cva';import { twMerge } from 'tailwind-merge';export const { compose, cva, cx: cn,} = defineConfig({ hooks: { onComplete: (className: string) => twMerge(className), },});export type { VariantProps };Copy and paste the following code into your project.
'use client';import { mergePropsN } from '@base-ui/react';import { Switch as SwitchPrimitive } from '@base-ui/react/switch';import { type PressEvents, usePress } from 'react-aria';import { cva, type VariantProps } from '@/lib/utils';const switchVariants = cva({ base: 'group/switch peer inline-flex shrink-0 items-center rounded-full shadow-xs transition-[shadow,background-color] focus-visible:outline-2 focus-visible:outline-primary focus-visible:outline-offset-2 data-unchecked:data-pressed:bg-primary/25 data-disabled:cursor-not-allowed data-checked:bg-primary data-unchecked:bg-input/80 data-disabled:opacity-50 data-unchecked:hover:bg-input', variants: { size: { sm: 'h-6 w-12', md: 'h-8 w-16', }, }, defaultVariants: { size: 'sm', },});export const switchThumbVariants = cva({ base: 'pointer-events-none block rounded-full border-2 border-ring/80 bg-background shadow-sm transition-[translate,transform,width,border-color] data-unchecked:translate-x-0 data-checked:border-primary', variants: { size: { sm: 'size-6 data-checked:translate-x-6 group-data-pressed/switch:not-data-disabled:w-6.5 data-checked:group-data-pressed/switch:translate-x-5.5', md: 'size-8 data-checked:translate-x-8 group-data-pressed/switch:not-data-disabled:w-8.5 data-checked:group-data-pressed/switch:translate-x-7.5', }, }, defaultVariants: { size: 'sm', },});/** * Represents the switch itself. * Renders a `<span>` element and a hidden `<input>` beside. * * Documentation: [Caprice UI Switch](https://caprice-ui.com/docs/components/switch) */export namespace Switch { export type Variants = VariantProps<typeof switchVariants>; export type Props = React.ComponentProps<typeof SwitchPrimitive.Root> & PressEvents & Switch.Variants; export type State = SwitchPrimitive.Root.State; export type ChangeEventDetails = SwitchPrimitive.Root.ChangeEventDetails; export type ChangeEventReason = SwitchPrimitive.Root.ChangeEventReason;}export function Switch({ className, size = 'sm', // PressEvents onPress, onPressStart, onPressChange, onPressEnd, onPressUp, onClick, ...props}: Switch.Props) { const { pressProps, isPressed } = usePress({ onPress, onPressStart, onPressChange, onPressEnd, onPressUp, onClick, isDisabled: props.disabled, }); const defaultProps: Switch.Props & { 'data-slot'?: string } = { 'data-slot': 'button', className: switchVariants({ size, className }), }; return ( <SwitchPrimitive.Root data-pressed={isPressed ? '' : undefined} {...mergePropsN([defaultProps, pressProps, props])} > <SwitchPrimitive.Thumb className={switchThumbVariants({ size })} data-slot="switch-thumb" /> </SwitchPrimitive.Root> );}Update the import paths to match your project setup.
Usage
import { Switch } from "@/components/caprice-ui/switch"import { Switch } from "@caprice-ui/react/switch"<Switch />