plugins/react-animation-studio/skills/framer-motion/SKILL.md
Expert knowledge for Framer Motion - the production-ready motion library for React. This skill enables declarative animations, gesture recognition, and layout animations with optimal performance.
npx skillsauth add markus41/claude framer-motionInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expert knowledge for Framer Motion - the production-ready motion library for React. This skill enables declarative animations, gesture recognition, and layout animations with optimal performance.
Activate this skill when:
.tsx or .jsx files containing motion componentslayoutIdAnimatePresence**/*.tsx containing framer-motion imports**/*.jsx containing motion components**/animations/*.ts with variants/transitions**/components/**/use*Animation*.tsnpm install framer-motion
# or
yarn add framer-motion
# or
pnpm add framer-motion
Replace HTML elements with motion. prefixed components:
import { motion } from 'framer-motion';
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
Define reusable animation states:
const variants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
<motion.div variants={variants} initial="hidden" animate="visible" />
Built-in gesture support:
<motion.button
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
whileFocus={{ outline: '2px solid blue' }}
whileDrag={{ cursor: 'grabbing' }}
/>
Automatic layout transitions:
<motion.div layout>
{/* Content that changes size/position */}
</motion.div>
// Shared element transitions
<motion.div layoutId="shared-element">...</motion.div>
Animate components when they mount/unmount:
import { AnimatePresence } from 'framer-motion';
<AnimatePresence mode="wait">
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
{
x: 100, // translateX (pixels)
y: 100, // translateY (pixels)
z: 100, // translateZ (pixels)
rotate: 90, // rotation (degrees)
rotateX: 90, // X-axis rotation
rotateY: 90, // Y-axis rotation
scale: 1.5, // uniform scale
scaleX: 1.5, // X-axis scale
scaleY: 1.5, // Y-axis scale
skew: 10, // skew (degrees)
originX: 0.5, // transform origin X (0-1)
originY: 0.5, // transform origin Y (0-1)
}
{
opacity: 0.5,
backgroundColor: '#ff0000',
color: '#ffffff',
borderRadius: 10,
boxShadow: '0px 10px 30px rgba(0,0,0,0.2)',
width: 200,
height: 200,
}
transition: {
type: 'tween',
duration: 0.3,
ease: 'easeInOut',
// Or custom cubic-bezier
ease: [0.25, 0.1, 0.25, 1],
}
transition: {
type: 'spring',
stiffness: 400, // Higher = faster
damping: 30, // Higher = less bounce
mass: 1, // Higher = more momentum
velocity: 0, // Initial velocity
}
transition: {
type: 'inertia',
velocity: 50,
power: 0.8,
timeConstant: 700,
bounceStiffness: 500,
bounceDamping: 10,
}
const container = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.2,
},
},
};
const item = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 },
};
<motion.ul variants={container} initial="hidden" animate="visible">
{items.map(i => (
<motion.li key={i} variants={item}>{i}</motion.li>
))}
</motion.ul>
const sequence = useAnimation();
async function playSequence() {
await sequence.start({ x: 100 });
await sequence.start({ y: 100 });
await sequence.start({ rotate: 180 });
}
<motion.div animate={sequence} />
import { useScroll, useTransform, motion } from 'framer-motion';
function ParallaxImage() {
const { scrollYProgress } = useScroll();
const y = useTransform(scrollYProgress, [0, 1], [0, -200]);
return <motion.img style={{ y }} />;
}
<motion.div
drag
dragConstraints={{ left: 0, right: 300, top: 0, bottom: 300 }}
dragElastic={0.2}
dragMomentum={false}
onDragEnd={(e, info) => console.log(info.offset, info.velocity)}
/>
import { useReducedMotion } from 'framer-motion';
function Component() {
const shouldReduceMotion = useReducedMotion();
return (
<motion.div
animate={shouldReduceMotion ? { opacity: 1 } : { opacity: 1, y: 0 }}
initial={shouldReduceMotion ? { opacity: 0 } : { opacity: 0, y: 20 }}
/>
);
}
<motion.div initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3, ease: 'easeOut' }}
<Card>{content}</Card> </motion.div>
</example>
<example>
Context: User needs a modal with enter/exit animations
user: "Make the modal animate when opening and closing"
assistant: Using AnimatePresence for mount/unmount animations:
```tsx
import { AnimatePresence, motion } from 'framer-motion';
<AnimatePresence>
{isOpen && (
<motion.div
key="modal-backdrop"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/50"
>
<motion.div
key="modal-content"
initial={{ opacity: 0, scale: 0.9, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.9, y: 20 }}
transition={{ type: 'spring', damping: 25, stiffness: 300 }}
>
{children}
</motion.div>
</motion.div>
)}
</AnimatePresence>
</example>
mcp__plugin_context7_context7__query-docs with framer-motion for latest APICreated by Brookside BI as part of React Animation Studio
tools
Managing project and task state in .claude/projects/{id}/ with atomic writes and session continuity
tools
Deep research before task execution using 4-source protocol: codebase→Perplexity→Context7→Firecrawl
tools
Validating task completion against acceptance criteria with per-type automated checks
tools
Using and creating project templates for webapp, API, ML pipeline, mobile, and infrastructure projects