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.
From react-animation-studionpx claudepluginhub markus41/claude --plugin react-animation-studioThis skill uses the workspace's default tool permissions.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Details PluginEval's skill quality evaluation: 3 layers (static, LLM judge), 10 dimensions, rubrics, formulas, anti-patterns, badges. Use to interpret scores, improve triggering, calibrate thresholds.
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