Loading...
Loading...
Framer Motion animation patterns and best practices for React. Page transitions, modal animations, gesture interactions, scroll-triggered effects, performance optimization, and accessibility. Use when creating animations, transitions, interactive UI components, micro-interactions, or implementing motion design in React/Next.js applications.
npx skill4agent add hoangminh46/mine-vibe framer-motion-magicProduction-ready animation patterns for stunning React UIs.
| Element | Usage |
|---|---|
| Animated div container |
| Interactive button |
| SVG animations |
| Wrap custom components |
| Prop | Purpose |
|---|---|
| Starting state (string or object) |
| Target state to animate to |
| State when unmounting (needs AnimatePresence) |
| Timing, easing, spring config |
| Named animation states |
| Type | When to Use |
|---|---|
| Natural, bouncy feel (default) |
| Precise timing control |
| Momentum-based (drag) |
// Spring (natural motion)
transition={{ type: "spring", stiffness: 300, damping: 20 }}
// Tween (precise)
transition={{ type: "tween", duration: 0.3, ease: "easeInOut" }}<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.3 }}
>
Content
</motion.div><motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, ease: "easeOut" }}
>
Content
</motion.div><motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
>
Click me
</motion.button>const containerVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: { staggerChildren: 0.1 }
}
};
const itemVariants = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0 }
};
<motion.ul variants={containerVariants} initial="hidden" animate="visible">
{items.map(item => (
<motion.li key={item.id} variants={itemVariants}>
{item.name}
</motion.li>
))}
</motion.ul>import { AnimatePresence, motion } from "framer-motion";
<AnimatePresence>
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Modal content
</motion.div>
)}
</AnimatePresence>| Mode | Behavior |
|---|---|
| Enter/exit simultaneously (default) |
| Wait for exit before enter |
| Pop exiting element from layout flow |
// In _app.tsx or layout.tsx
<AnimatePresence mode="wait" initial={false}>
<motion.div
key={router.pathname}
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -20 }}
transition={{ duration: 0.3 }}
>
<Component {...pageProps} />
</motion.div>
</AnimatePresence>| Prop | Trigger |
|---|---|
| Mouse enter |
| Mouse down / touch |
| Element focused |
| During drag |
| Element in viewport |
<motion.div
drag
dragConstraints={{ left: -100, right: 100, top: -50, bottom: 50 }}
dragElastic={0.2}
whileDrag={{ scale: 1.1 }}
/>const controls = useDragControls();
<div onPointerDown={(e) => controls.start(e)}>
Drag handle
</div>
<motion.div drag="x" dragControls={controls}>
Draggable content
</motion.div><motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.5 }}
>
Animates when scrolled into view
</motion.div>const { scrollYProgress } = useScroll();
// Animate based on scroll
<motion.div style={{ scaleX: scrollYProgress }} />const { scrollYProgress } = useScroll();
const y = useTransform(scrollYProgress, [0, 1], [0, -200]);
<motion.div style={{ y }}>
Parallax content
</motion.div>const x = useMotionValue(0);
<motion.div style={{ x }} drag="x" />const x = useMotionValue(0);
const opacity = useTransform(x, [-100, 0, 100], [0, 1, 0]);
const background = useTransform(
x,
[-100, 0, 100],
["#ff0000", "#ffffff", "#00ff00"]
);const x = useMotionValue(0);
const springX = useSpring(x, { stiffness: 300, damping: 30 });const x = useMotionValue(0);
const velocityX = useVelocity(x);
const scale = useTransform(velocityX, [-500, 0, 500], [0.8, 1, 0.8]);const ref = useRef(null);
const isInView = useInView(ref, { once: true });
<motion.div
ref={ref}
initial={{ opacity: 0 }}
animate={isInView ? { opacity: 1 } : {}}
/><motion.div layout>
{/* Size/position changes animate automatically */}
</motion.div>// Card in list
<motion.div layoutId={`card-${id}`}>
<h2>{title}</h2>
</motion.div>
// Expanded card (same layoutId)
<motion.div layoutId={`card-${id}`}>
<h2>{title}</h2>
<p>{description}</p>
</motion.div>| Value | Animates |
|---|---|
| Size + position |
| Only position |
| Only size |
const cardVariants = {
initial: { opacity: 0, y: 20 },
animate: { opacity: 1, y: 0 },
hover: { scale: 1.02, boxShadow: "0 10px 30px rgba(0,0,0,0.2)" },
tap: { scale: 0.98 },
exit: { opacity: 0, y: -20 }
};
<motion.div
variants={cardVariants}
initial="initial"
animate="animate"
whileHover="hover"
whileTap="tap"
exit="exit"
/>const containerVariants = {
hidden: {},
visible: {
transition: {
staggerChildren: 0.1,
delayChildren: 0.3
}
}
};
const itemVariants = {
hidden: { opacity: 0, x: -20 },
visible: { opacity: 1, x: 0 }
};| ✅ Use | ❌ Avoid |
|---|---|
| |
| |
|
| Tip | Implementation |
|---|---|
| Use transform over layout properties | |
| Avoid onUpdate callback | Triggers every frame |
| Use layout prop sparingly | Can cause reflows |
| Lazy load with useInView | Animate only when visible |
| Don't animate too many elements | Limit to 10-20 simultaneous |
// ❌ Avoid
<motion.div onUpdate={(latest) => console.log(latest)} />
<motion.div style={{ x: motionValue }} /> // MotionValue in style
transition={{ repeatDelay: 1 }} // repeatDelay
transition={{ damping: 0 }} // Zero damping// Option 1: MotionConfig (Recommended)
<MotionConfig reducedMotion="user">
<App />
</MotionConfig>
// Option 2: useReducedMotion hook
const shouldReduce = useReducedMotion();
<motion.div
animate={shouldReduce ? { opacity: 1 } : { opacity: 1, y: 0 }}
/>| Value | Behavior |
|---|---|
| Respect OS preference |
| Always reduce motion |
| Ignore preference |
opacitybackgroundColortransform| Name | Feel |
|---|---|
| Slow start |
| Slow end (most natural) |
| Slow both ends |
| Constant speed |
| Pull back then forward |
| Overshoot effect |
| Feel | Config |
|---|---|
| Snappy | |
| Bouncy | |
| Smooth | |
| Gentle | |
| Animation Type | Duration |
|---|---|
| Micro-interactions | 100-200ms |
| UI feedback | 200-300ms |
| Transitions | 300-500ms |
| Page transitions | 400-600ms |
| Complex sequences | 500-1000ms |
Remember: Animations should enhance UX, not distract. Use motion purposefully to guide attention, provide feedback, and create delight.