Animated Checkbox
A checkbox whose checkmark draws itself in as a real SVG path animation.
- Category
- Components
- Added
- 2026-08-22
- Deps
- 1
- Updated
- 2026-09-01
Props
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
export function AnimatedCheckbox({
label = "Send me the changelog",
defaultChecked = false,
}: {
label?: string;
defaultChecked?: boolean;
}) {
const [checked, setChecked] = useState(defaultChecked);
return (
<label className="flex cursor-pointer items-center gap-3 select-none">
<span
role="checkbox"
aria-checked={checked}
tabIndex={0}
onClick={() => setChecked((v) => !v)}
onKeyDown={(e) => {
if (e.key === " " || e.key === "Enter") {
e.preventDefault();
setChecked((v) => !v);
}
}}
className="relative flex h-6 w-6 shrink-0 items-center justify-center rounded-md border-2 border-neutral-300 transition-colors"
style={{ borderColor: checked ? "#ef4444" : undefined, backgroundColor: checked ? "#ef4444" : "transparent" }}
>
<motion.svg viewBox="0 0 24 24" className="h-4 w-4" fill="none">
<motion.path
d="M5 13l4 4L19 7"
stroke="white"
strokeWidth={3}
strokeLinecap="round"
strokeLinejoin="round"
initial={false}
animate={{ pathLength: checked ? 1 : 0, opacity: checked ? 1 : 0 }}
transition={{ duration: 0.3, ease: [0.65, 0, 0.35, 1] }}
/>
</motion.svg>
</span>
<span className="text-sm font-medium">{label}</span>
</label>
);
}
About this component
A native checkbox gives no room to signal that a choice actually registered beyond a color flip, and most custom checkbox implementations that try to fix this just swap in a static check icon, which reads the same way. This one animates the SVG path's pathLength from 0 to 1 so the tick draws itself in stroke by stroke, paired with an opacity fade so there is no flash of a fully-drawn-but-invisible path. Reach for it over a plain CSS checkbox when that draw-in motion is worth the framer-motion import; in a dense settings list with dozens of checkboxes, the visual noise of that many ticks drawing in at once argues for a plain native element instead. The part worth noticing: this is not a hidden native input with a styled sibling, it is a role="checkbox" span with manual Space/Enter handling, because a native input cannot host the SVG check mark as its own animatable child the way pathLength needs. One real limitation: it does not wire into a native form's checked/value submission, so form libraries need to be pointed at it explicitly.
Curator’s note
pathLength is the whole trick — animating opacity or swapping an icon looks fine in a screenshot but reads as flat next to an actual stroke draw-in. It's one prop on the path, easy to skip, obvious once you see it.
Pairs well with
Matched on shared tags and category — the entries most likely to be used alongside this one.
Interactive Star Rating
componentsA 5-star rating control with hover preview and a springy fill pop.
Theme Toggle Sun Moon Morph
componentsA track+thumb switch whose icon morphs between a sun and a crescent moon.
Segmented Morph Toggle
componentsA segmented control where only the selected option expands to its label.