Framer Motion Skill
SkillAI & modelsAnimation library for React. Declarative, physics-based, gesture-aware.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Framer Motion Skill skill
About this capability
Specialized agents, MCP servers, skills, and a visual dashboard for Claude Code. MIT.
What this skill tells your AI
The instructions your AI receives, as published by claude-dev-suite/claude-dev-suite in skills/animation/framer-motion/SKILL.md and read by ahel’s review.
Animation library for React. Declarative, physics-based, gesture-aware.
Install
npm install framer-motion
Core API
| API | Purpose |
|---|---|
motion.div | Animatable element |
animate prop | Target state |
initial prop | Starting state |
exit prop | Unmount state (requires AnimatePresence) |
variants | Named animation states, enables orchestration |
transition | Timing, easing, spring config |
whileHover / whileTap / whileFocus | Gesture states |
drag | Drag gesture with constraints |
layout / layoutId | FLIP layout animation |
AnimatePresence | Animates unmounting components |
useScroll | Scroll progress values |
useTransform | Map one motion value to another |
useSpring | Spring-based motion value |
useInView | Observe element entering viewport |
useAnimation | Imperative animation control |
Variants & Orchestration
const container = {
hidden: {},
visible: { transition: { staggerChildren: 0.08, delayChildren: 0.1 } }
};
const item = {
hidden: { opacity: 0, y: 24 },
visible: { opacity: 1, y: 0, transition: { duration: 0.4, ease: "easeOut" } }
};
<motion.ul variants={container} initial="hidden" whileInView="visible" viewport={{ once: true }}>
{items.map(i => <motion.li key={i} variants={item}>{i}</motion.li>)}
</motion.ul>
AnimatePresence
<AnimatePresence mode="wait">
{isVisible && (
<motion.div
key="unique-key"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.2 }}
/>
)}
</AnimatePresence>
mode="wait" — exits finish before new element enters.
mode="popLayout" — exiting element pops out of layout flow.
Layout Animation (FLIP)
// Automatic smooth repositioning
<motion.div layout />
// Shared element transition across routes/conditions
<motion.div layoutId="card-thumbnail" /> // source
<motion.div layoutId="card-thumbnail" /> // destination — auto-animates
Scroll-linked
const { scrollYProgress } = useScroll({ target: ref, offset: ["start end", "end start"] });
const y = useTransform(scrollYProgress, [0, 1], [0, -100]);
const opacity = useTransform(scrollYProgress, [0, 0.3, 1], [0, 1, 0]);
Spring Config
// Bouncy entrance
transition={{ type: "spring", stiffness: 400, damping: 20 }
// Gentle
transition={{ type: "spring", stiffness: 100, damping: 30 }
// No bounce
transition={{ type: "spring", stiffness: 300, damping: 50 }
Gestures
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.97 }}
transition={{ type: "spring", stiffness: 400, damping: 20 }}
>
Click me
</motion.button>
// Drag with constraints
<motion.div
drag
dragConstraints={{ left: -100, right: 100, top: -50, bottom: 50 }}
dragElastic={0.2}
/>
Imperative Control
const controls = useAnimation();
await controls.start({ opacity: 1, y: 0, transition: { duration: 0.4 } });
controls.stop();
<motion.div animate={controls} initial={{ opacity: 0, y: 20 }} />
Reduced Motion
import { useReducedMotion } from "framer-motion";
function AnimatedCard() {
const reduced = useReducedMotion();
return (
<motion.div
initial={{ opacity: 0, y: reduced ? 0 : 20 }}
animate={{ opacity: 1, y: 0 }}
/>
);
}
Common Patterns
// Stagger list reveal on scroll
// Page transition wrapper
// Accordion with layout animation
// Drag-and-drop with layoutId
// Hero → detail shared element
Pitfalls
exitonly works inside<AnimatePresence>— wrap the closest conditional parentlayouton elements that change children count can cause jumps — uselayoutIdinstead- Spring animations ignore
duration— usestiffness/damping useScrollwithouttargettracks window scroll- Always set
viewport={{ once: true }}for entry animations to avoid re-triggering on scroll-up
Signals
- GitHub stars
- 33
- Forks
- 6
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
framer-motion-claude-dev-suite- Source
- github.com/claude-dev-suite/claude-dev-suite