motion (formerly framer motion)
Web animations for React and JavaScript
$ npx docs2skills add motion-animationsMotion (formerly Framer Motion)
Production-ready motion library for React and vanilla JavaScript
What this skill does
Motion is a declarative animation library that makes complex animations simple through component-based APIs. It handles the heavy lifting of performance optimization, gesture recognition, and layout calculations while providing an intuitive interface for creating everything from simple transitions to complex interactive experiences.
Developers choose Motion over CSS animations or other libraries because it provides automatic hardware acceleration, seamless layout animations, powerful gesture handling, and scroll-triggered animations out of the box. It solves the common problems of janky animations, complex state management during transitions, and cross-browser compatibility issues.
Prerequisites
- React 16.8+ (for React usage) or modern browser with ES6 support (for vanilla JS)
- Node.js 14+ for development
- TypeScript support included by default
- No external dependencies or API keys required
Quick start
npm install motion
import { motion } from "motion/react"
function App() {
return (
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
Hello Motion!
</motion.div>
)
}
Core concepts
Motion components: Any HTML/SVG element can become animated by prefixing with motion. (e.g., motion.div, motion.svg). These components accept animation props alongside standard HTML attributes.
Animation props: initial sets starting values, animate defines target values, exit handles unmounting animations, and transition controls timing and easing.
Values and transforms: Motion automatically handles CSS transforms, opacity, colors, and layout properties. Values can be numbers, strings with units, or keyframe arrays.
Animation lifecycle: Animations are driven by prop changes. When animate values change, Motion automatically transitions from current to new values.
Key API surface
// Basic animation props
<motion.div
initial={{ x: 0 }} // Starting state
animate={{ x: 100 }} // Target state
exit={{ x: -100 }} // Exit animation
transition={{ duration: 1 }} // Timing control
/>
// Gesture handlers
<motion.div
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
drag="x"
dragConstraints={{ left: 0, right: 300 }}
/>
// Layout animations
<motion.div layout />
// Scroll-triggered animations
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
/>
// Animation controls
const controls = useAnimationControls()
controls.start({ x: 100 })
// Motion values
const x = useMotionValue(0)
const opacity = useTransform(x, [0, 100], [1, 0])
Common patterns
Staggered list animations:
<motion.ul
initial="hidden"
animate="visible"
variants={{
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { staggerChildren: 0.1 }
}
}}
>
{items.map(item => (
<motion.li
key={item.id}
variants={{
hidden: { y: 20, opacity: 0 },
visible: { y: 0, opacity: 1 }
}}
>
{item.text}
</motion.li>
))}
</motion.ul>
Page transitions with AnimatePresence:
import { AnimatePresence } from "motion/react"
<AnimatePresence mode="wait">
<motion.div
key={router.pathname}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
>
{page}
</motion.div>
</AnimatePresence>
Draggable with physics:
<motion.div
drag
dragConstraints={{ left: 0, right: 300, top: 0, bottom: 300 }}
dragElastic={0.2}
dragTransition={{ bounceStiffness: 600, bounceDamping: 10 }}
/>
Scroll-linked animations:
const { scrollYProgress } = useScroll()
const scale = useTransform(scrollYProgress, [0, 1], [0.8, 1.2])
<motion.div style={{ scale }} />
Configuration
Global transition defaults:
<MotionConfig transition={{ duration: 0.3, ease: "easeOut" }}>
<App />
</MotionConfig>
Reduced motion support:
<MotionConfig reducedMotion="user">
// Respects prefers-reduced-motion
</MotionConfig>
Performance optimization:
// Use transform properties for better performance
animate={{ x: 100, scale: 1.2 }} // Good
animate={{ left: 100, width: 200 }} // Avoid if possible
// Enable GPU acceleration
style={{ willChange: "transform" }}
Best practices
- Use transform properties (x, y, scale, rotate) instead of layout properties (left, top, width, height) for better performance
- Wrap conditional renders with
<AnimatePresence>to enable exit animations - Use
layoutprop for automatic layout animations when element dimensions change - Leverage
variantsfor complex, reusable animation states - Use
useMotionValuefor values that update frequently without re-renders - Apply
dragConstraintsto prevent dragged elements from leaving their container - Use
viewport={{ once: true }}for scroll animations that should only trigger once - Combine
whileHoverandwhileTapfor better button feedback - Use
staggerChildrenin parent variants for coordinated list animations - Enable
reducedMotionsupport for accessibility compliance
Gotchas and common mistakes
AnimatePresence placement: Must wrap the component that conditionally renders, not the conditional component itself. Place it outside the condition.
Layout thrashing: Animating width, height, left, top causes layout recalculation. Use transform properties or the layout prop instead.
Exit animations not working: Requires <AnimatePresence> wrapper and unique key props on animated components.
Drag constraints: dragConstraints expects an object with pixel values or a ref to a DOM element, not CSS strings.
Initial prop timing: initial={false} disables mount animations entirely. Use initial with same values as animate to start animations immediately.
Motion values and React state: useMotionValue doesn't trigger re-renders. Use useMotionValueEvent to sync with React state if needed.
SVG transformOrigin: SVG elements may need explicit transformOrigin values as browser defaults vary.
Layout animations and positioning: Elements with position: absolute may not animate layouts correctly. Use layout="position" instead of layout for positioned elements.
Gesture conflicts: Multiple gesture props (drag, whileHover, whileTap) can interfere. Test interactions thoroughly on touch devices.
Memory leaks with motion values: Motion values persist beyond component unmounting. Clean up manually in useEffect cleanup if creating many programmatically.