Switch
trovecn/switchFreeComponent
Base UI switch with a squishy, draggable, spring-driven thumb.
Install it
npx shadcn@latest add https://trovecn.dev/r/switch.jsonSource
1"use client";23import { useEffect, useRef, useState } from "react";4import { Switch as SwitchPrimitive } from "@base-ui/react/switch";5import { motion, useMotionValue, useReducedMotion, animate } from "motion/react";67import { cn } from "@/lib/utils";8import { spring } from "@/lib/springs";910const SIZES = {11 default: { track: 34, height: 20, thumb: 16 },12 sm: { track: 24, height: 14, thumb: 12 },13} as const;1415const DRAG_DEAD_ZONE = 2;1617function Switch({18 className,19 size = "default",20 checked: checkedProp,21 defaultChecked,22 onCheckedChange,23 disabled = false,24 ...props25}: Omit<SwitchPrimitive.Root.Props, "inputRef"> & {26 size?: "sm" | "default";27}) {28 const { track, height, thumb } = SIZES[size];29 const offset = (height - thumb) / 2;30 const travel = track - thumb - offset * 2;31 const hoverExtend = (2 * thumb) / 16;32 const pressExtend = (4 * thumb) / 16;33 const pressShrink = (4 * thumb) / 16;3435 const [uncontrolledChecked, setUncontrolledChecked] = useState(defaultChecked ?? false);36 const checked = checkedProp ?? uncontrolledChecked;3738 const applyChecked = (next: boolean, eventDetails?: SwitchPrimitive.Root.ChangeEventDetails) => {39 if (checkedProp === undefined) setUncontrolledChecked(next);40 onCheckedChange?.(next, eventDetails as SwitchPrimitive.Root.ChangeEventDetails);41 };4243 const [hovered, setHovered] = useState(false);44 const [pressed, setPressed] = useState(false);45 const dragging = useRef(false);46 const suppressNextClick = useRef(false);47 const dragStart = useRef<{ clientX: number; originX: number } | null>(null);4849 const thumbWidth = pressed ? thumb + pressExtend : hovered ? thumb + hoverExtend : thumb;50 const thumbHeight = pressed ? thumb - pressShrink : thumb;51 const thumbY = pressed ? offset + pressShrink / 2 : offset;52 const thumbX = checked ? offset + travel - (thumbWidth - thumb) : offset;5354 const motionX = useMotionValue(thumbX);55 const reduceMotion = useReducedMotion();56 const hasMountedRef = useRef(false);57 useEffect(() => {58 hasMountedRef.current = true;59 }, []);6061 useEffect(() => {62 if (dragging.current) return;63 if (!hasMountedRef.current) {64 motionX.set(thumbX);65 return;66 }67 const controls = animate(motionX, thumbX, spring.moderate.enter);68 return () => controls.stop();69 // eslint-disable-next-line react-hooks/exhaustive-deps70 }, [thumbX]);7172 return (73 <SwitchPrimitive.Root74 data-slot="switch"75 data-size={size}76 checked={checked}77// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from trovecn
All 26 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | trovecn | Components | Free | 3 deps | |
| Agent Activityagent-activity | trovecn | Components | Free | 2 deps | |
| Approval Requestapproval-request | trovecn | Components | Free | 2 deps | |
| Buttonbutton | trovecn | Components | Free | 2 deps | |
| Checkboxcheckbox | trovecn | Components | Free | 2 deps | |
| Checkbox Groupcheckbox-group | trovecn | Components | Free | 2 deps | |
| Comboboxcombobox | trovecn | Components | Free | 3 deps | |
| Comparison Slidercomparison-slider | trovecn | Components | Free | 1 dep |
