Switch
Tactile toggle switch with haptic-feel spring bounce, icon slots, and keyboard accessibility.
ReactTailwind CSSFramer Motion
Dependenciesframer-motion, lucide-react
npm install framer-motion lucide-reactPreview
Hardware AccelerationUtilize discrete GPU rendering passes for fluid animations.
Source Code
switch.tsxtsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
"use client";
import React, { useState } from "react";
import { motion } from "framer-motion";
import { Check } from "lucide-react";
export interface SwitchProps {
checked?: boolean;
defaultChecked?: boolean;
onCheckedChange?: (checked: boolean) => void;
label?: string;
description?: string;
disabled?: boolean;
}
export function Switch({
defaultChecked = false,
checked: controlledChecked,
onCheckedChange,
label,
description,
disabled = false,
}: SwitchProps) {
const [internalChecked, setInternalChecked] = useState(defaultChecked);
const isChecked = controlledChecked !== undefined ? controlledChecked : internalChecked;
const toggle = () => {
if (disabled) return;
const next = !isChecked;
setInternalChecked(next);
onCheckedChange?.(next);
};
return (
<div className="flex items-center justify-between gap-4 max-w-sm">
{label && <span className="text-xs font-semibold">{label}</span>}
<button
type="button"
role="switch"
aria-checked={isChecked}
onClick={toggle}
className={`relative inline-flex h-6 w-11 shrink-0 rounded-full p-0.5 transition-colors ${
isChecked ? "bg-zinc-900" : "bg-zinc-200"
}`}
>
<motion.span
layout
className={`h-5 w-5 rounded-full bg-white shadow-sm ${
isChecked ? "translate-x-5" : "translate-x-0"
}`}
/>
</button>
</div>
);
}Usage
usage.tsxtsx
1
2
3
4
5
<Switch
label="Hardware Acceleration"
description="Enable GPU rasterization for smooth animation."
onCheckedChange={(checked) => console.log(checked)}
/>Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| checked | boolean | Optional | undefined | Controlled checked state. |
| defaultChecked | boolean | Optional | false | Uncontrolled initial checked state. |
| label | string | Optional | undefined | Primary label displayed alongside the switch. |
| onCheckedChange | (checked: boolean) => void | Optional | undefined | Callback triggered when the switch value changes. |
Types
types.tstsx
1
2
3
4
5
6
7
8
export interface SwitchProps {
checked?: boolean;
defaultChecked?: boolean;
onCheckedChange?: (checked: boolean) => void;
label?: string;
description?: string;
disabled?: boolean;
}