Switch
The Switch component prioritizes instantaneous state reflection. I opted for a calibrated `ease-out` curve rather than a physics-based `spring`. While springs offer a delightful 'bounce,' they introduce perceived latency in a binary toggle where the user expects immediate confirmation. Since this component is triggered by a click rather than a gesture-based drag, the 'bounce' felt disconnected from the user's physical input. Technically, I omitted internal symbols (like 'on/off' icons) to favor component composability. By keeping the 'thumb' and 'track' as clean primitives, the library remains unopinionated. This shifts the responsibility of branding to the implementation layer, ensuring the component doesn't become 'prop-heavy' as users request different icon sets or labels.
Props & Styling
Props
Switch currently takes no props — isOn is internal (useState(true)), so this ships as a fixed, uncontrolled demo primitive rather than a controllable form component. To use it in a real form, add checked/defaultChecked/onCheckedChange props and apply the same controlled-with-fallback pattern used by RadioGroup and Modal (external ?? internal state).
Styling
The track's background color cross-fades directly (animate={{ backgroundColor }}), while the thumb's translateX uses a spring (stiffness: 300, damping: 30) — the one place in the library a spring drives a click-triggered toggle rather than a hover/drag gesture. It's intentional here because the motion represents a physical slide from one side to the other, not a decision being confirmed; contrast with Modal or Accordion, which use easeOut specifically because a spring's bounce would read as latency on a binary state change.
Code
import { motion} from 'motion/react';
import { useState } from "react";
const MotionDiv = motion.create('div');
const Switch = () => {
const [isOn, setIsOn] = useState(true);
const toggleSwitch = () => {
setIsOn(!isOn);
}
return (
<MotionDiv
className = "w-10 h-5 rounded-full flex items-center cursor-pointer relative p-1"
onClick={toggleSwitch}
animate = {{backgroundColor: isOn ? '#0f327c' : '#e5e7eb'}}
>
<MotionDiv
initial = {{translateX: 20}}
animate = {{translateX: isOn ? 20 : 0}}
transition = {{type: 'spring', stiffness: 300, damping: 30}}
className = "bg-white w-5 h-5 rounded-full absolute left-0"
/>
</MotionDiv>
)
}
export default Switch