Motion Animation Engineer
When This Skill Activates
Activate when the task involves:
- Declarative UI animation in React or Vue
- Component mount/unmount/exit transitions
- Gesture interactions (drag, hover, tap, pan)
- Layout shifts, responsive reflow, shared element transitions
- Scroll-driven or viewport-triggered component effects
- Spring physics, inertia, or motion values
- Mobile-optimized micro-interactions with strict bundle budgets
Core Principles
- Declarative over imperative. Let the component props drive state. Use
initial,animate,exit, andvariantsso the animation state is readable from the JSX. - Hardware acceleration only. Animate
transformandopacitywhenever possible. Motion handles scale correction during layout animations automatically—trust it, but avoid animatingwidth,height,top,left. - Bundle is a feature. Use
LazyMotion+domAnimationor theanimate()mini (2.6KB) for performance-critical paths. The full React package is ~59KB gzipped; the core is ~18KB. Only load what the viewport needs.
Workflow
Step 1: Select API Level
Choose the abstraction based on complexity and framework:
| Need | API | Import | Size |
|---|---|---|---|
| Simple tween | animate() mini |
motion/animate |
2.6KB |
| React component | motion components |
motion/react |
~18KB core |
| Full gestures + layout | motion + hooks |
motion/react |
~59KB |
| Vue component | motion components |
motion/vue |
~59KB |
v12 Import Rule: Use motion/react, not framer-motion. The old namespace still works but is deprecated.
Step 2: Structure Variants
Define animation states as variant objects, not inline props. This enables orchestration, stagger, and reuse.
const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { staggerChildren: 0.1, delayChildren: 0.2 }
}
};
const itemVariants = {
hidden: { y: 20, opacity: 0 },
visible: { y: 0, opacity: 1 }
};
Apply with variants={containerVariants} on parent and child motion elements.
Step 3: Handle Presence
For mount/unmount animations, wrap the component tree in AnimatePresence and give each child a unique key.
<<AnimatePresence mode="wait">
{isOpen && (
<motion.div
key="modal"
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
>
{content}
</motion.div>
)}
</AnimatePresence>
Use mode="wait" to delay entering until exit completes. Use mode="popLayout" for list reordering.
Step 4: Enable Layout Animations
For responsive reflow or drag-to-reorder, add the layout prop. For shared element transitions between routes or states, use layoutId with the same string on both source and target.
<motion.div layout layoutId="card" transition={{ type: "spring", stiffness: 300, damping: 30 }} />
Wrap related layout elements in <LayoutGroup> to scope measurements.
Step 5: Add Gestures
Use gesture props directly on motion components:
whileHover={{ scale: 1.02 }}whileTap={{ scale: 0.98 }}drag="x"ordrag(both axes)dragConstraints={{ left: 0, right: 0 }}dragElastic={0.2}
For custom gesture logic, use useMotionValue and useTransform to create reactive chains.
Step 6: Scroll Integration
Use hooks, not manual listeners:
const { scrollYProgress } = useScroll();
const scale = useTransform(scrollYProgress, [0, 1], [1, 1.5]);
For viewport-triggered reveals, use useInView with once: true to fire once.
Step 7: Optimize for Mobile & Web
- Mobile: Use
animate()mini for essential micro-interactions. Avoid heavy blur/backdrop-filter animations. Test on low-end Android. - Web: Use
LazyMotionto split animation features by route. Defer non-critical variants. - Accessibility: Always respect
prefers-reduced-motion. Provide instant state changes when reduced motion is preferred.
const prefersReducedMotion = useReducedMotion(); // from motion/react
const transition = prefersReducedMotion ? { duration: 0 } : { type: "spring" };
Output Format
React Component Template
import { motion, AnimatePresence, useReducedMotion } from "motion/react";
export function AnimatedComponent({ isVisible, children }) {
const reduced = useReducedMotion();
return (
<AnimatePresence mode="wait">
{isVisible && (
<motion.div
key="content"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={reduced ? { duration: 0 } : { type: "spring", stiffness: 300 }}
>
{children}
</motion.div>
)}
</AnimatePresence>
);
}
Vue Component Template
<template>
<Motion
:initial="{ opacity: 0 }"
:animate="{ opacity: 1 }"
:transition="{ duration: 0.5 }"
>
<div>Content</div>
</Motion>
</template>
<script setup>
import { Motion } from "motion/vue";
</script>
Examples
Example 1: Staggered List Entrance
Input: User wants a todo list to animate in on load.
Output: Parent with staggerChildren variant, children with itemVariants. Use AnimatePresence for item deletion.
Example 2: Drag-to-Reorder
Input: User wants a sortable grid.
Output: Each item is motion.div with layout and drag. Parent uses LayoutGroup. Use Reorder.Group and Reorder.Item from Motion for simplified API.
Example 3: Scroll-Linked Parallax
Input: User wants a hero image to scale as they scroll.
Output: useScroll + useTransform bound to scale and y. Apply to motion.img.
Anti-Patterns
❌ Don't: Animate layout properties directly
// Bad: triggers layout thrashing
<motion.div animate={{ width: open ? 300 : 100 }} />
✅ Do: Use layout animations or transforms
// Good: GPU-accelerated, measured automatically
<motion.div layout style={{ width: open ? 300 : 100 }} />
❌ Don't: Forget AnimatePresence keys
Missing key causes exit animations to fail silently.
❌ Don't: Import from "framer-motion" in new projects
Use motion/react for v12+. The old package name is legacy.
Tool Usage
- Use
Readto inspect existing component files before adding animation. - Use
Writeto create new animated components ormotionwrappers. - Use
Editto retrofitAnimatePresenceorlayoutprops into existing JSX. - Use
Bashto check bundle size vianpm ls motionor build analyzer.
Integration & Synergy
- With GSAP: Use Motion for UI layer (modals, drawers, gestures). Use GSAP for scroll sequences and text reveals. See
animation-hybrid-architectskill for boundary protocols. - With Tailwind CSS: Combine
classNameutilities withmotionprops. No conflicts. - With React Three Fiber: Use
framer-motion-3dfor 3D declarative animation. - With Next.js: Wrap page components in
AnimatePresencein_app.jsor root layout for route transitions.
References
references/react-patterns.md— Advanced variants, orchestration, and Reorder APIreferences/vue-patterns.md— Motion for Vue specifics, composable patternsreferences/performance-budgets.md— LazyMotion, mini API, bundle splitting strategies, 5KB runtime constraintsreferences/motion-plus.md— Motion+ Carousel, Ticker, Cursor, AnimateNumber integration