Mastering Animations in React with Framer Motion
Create stunning, performant animations in your React applications with Framer Motion. From basic transitions to complex gesture-based interactions.
Neha Verma
Frontend Developer
February 28, 2025
9 min read

Mastering Animations in React with Framer Motion
Animations breathe life into web applications, transforming static interfaces into engaging experiences. Framer Motion has emerged as the go-to animation library for React, offering powerful features with an intuitive API.
Why Framer Motion?
Traditional CSS animations and JavaScript-based libraries often require complex code for simple effects. Framer Motion simplifies this:
- Declarative syntax: Animations as props
- Performance: Hardware-accelerated by default
- Gestures: Built-in drag, tap, and hover support
- Spring physics: Natural, fluid motion
- Layout animations: Automatic smooth transitions
Getting Started
Installation
npm install framer-motion
Basic Animation
import { motion } from "framer-motion";
export default function FadeIn() {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.6 }}
>
Content fades in
</motion.div>
);
}
Essential Animation Patterns
1. Entrance Animations
Create engaging page load experiences:
export default function Hero() {
return (
<div>
<motion.h1
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
>
Welcome to Star Works
</motion.h1>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
>
Transform your digital presence
</motion.p>
<motion.button
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.4, delay: 0.6 }}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
Get Started
</motion.button>
</div>
);
}
2. Scroll-Triggered Animations
Animate elements as they enter the viewport:
export default function ScrollReveal() {
return (
<motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, amount: 0.3 }}
transition={{ duration: 0.6 }}
>
This appears when scrolled into view
</motion.div>
);
}
3. Staggered Animations
Create cascading effects for lists:
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
const item = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0 },
};
export default function StaggeredList() {
return (
<motion.ul variants={container} initial="hidden" animate="show">
{items.map((item, i) => (
<motion.li key={i} variants={item}>
{item}
</motion.li>
))}
</motion.ul>
);
}
4. Interactive Hover Effects
export default function InteractiveCard() {
return (
<motion.div
className="card"
whileHover={{
scale: 1.05,
boxShadow: "0 10px 40px rgba(195, 240, 15, 0.3)",
}}
whileTap={{ scale: 0.98 }}
transition={{ type: "spring", stiffness: 300 }}
>
Hover over me
</motion.div>
);
}
5. Drag Interactions
export default function DraggableElement() {
return (
<motion.div
drag
dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }}
dragElastic={0.2}
whileDrag={{ scale: 1.1, cursor: "grabbing" }}
>
Drag me around
</motion.div>
);
}
Advanced Techniques
Layout Animations
Automatically animate layout changes:
import { AnimatePresence, motion } from "framer-motion";
export default function ExpandableCard() {
const [isExpanded, setIsExpanded] = useState(false);
return (
<motion.div
layout
onClick={() => setIsExpanded(!isExpanded)}
className="card"
>
<motion.h2 layout>Title</motion.h2>
<AnimatePresence>
{isExpanded && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Additional content
</motion.div>
)}
</AnimatePresence>
</motion.div>
);
}
Path Animations (SVG)
Animate SVG paths for dynamic graphics:
export default function AnimatedLogo() {
return (
<svg width="200" height="200">
<motion.path
d="M10 10 L190 190"
stroke="#C3F00F"
strokeWidth="4"
fill="transparent"
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{ duration: 2, ease: "easeInOut" }}
/>
</svg>
);
}
Custom Spring Animations
export default function BouncyButton() {
return (
<motion.button
whileHover={{ scale: 1.1 }}
transition={{
type: "spring",
stiffness: 400,
damping: 10,
}}
>
Bouncy!
</motion.button>
);
}
Performance Best Practices
1. Use Transform Properties
Animate transform and opacity for best performance:
// Good - GPU accelerated
<motion.div animate={{ x: 100, scale: 1.2, opacity: 0.5 }} />
// Avoid - triggers layout recalculation
<motion.div animate={{ width: 200, marginLeft: 50 }} />
2. Optimize with layoutId
For smooth shared element transitions:
<motion.div layoutId="unique-id">Shared element</motion.div>
3. Reduce Motion for Accessibility
Respect user preferences:
import { useReducedMotion } from "framer-motion";
export default function AccessibleAnimation() {
const shouldReduceMotion = useReducedMotion();
return (
<motion.div
initial={{ opacity: 0, y: shouldReduceMotion ? 0 : 20 }}
animate={{ opacity: 1, y: 0 }}
>
Content
</motion.div>
);
}
Real-World Example: Animated Navigation
export default function AnimatedNav() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<motion.button
onClick={() => setIsOpen(!isOpen)}
animate={{ rotate: isOpen ? 90 : 0 }}
>
Menu
</motion.button>
<AnimatePresence>
{isOpen && (
<motion.nav
initial={{ x: "100%" }}
animate={{ x: 0 }}
exit={{ x: "100%" }}
transition={{ type: "spring", damping: 20 }}
>
<motion.ul
variants={{
open: {
transition: { staggerChildren: 0.07, delayChildren: 0.2 },
},
closed: {
transition: { staggerChildren: 0.05, staggerDirection: -1 },
},
}}
initial="closed"
animate="open"
exit="closed"
>
{menuItems.map((item, i) => (
<motion.li
key={i}
variants={{
open: { y: 0, opacity: 1 },
closed: { y: 50, opacity: 0 },
}}
>
{item}
</motion.li>
))}
</motion.ul>
</motion.nav>
)}
</AnimatePresence>
</>
);
}
Common Pitfalls
1. Over-Animation
Don't animate everything. Be selective:
- ✅ Important state changes
- ✅ User interactions
- ✅ Page transitions
- ❌ Every hover effect
- ❌ Continuous background animations
2. Ignoring Performance
Monitor frame rates:
import { useAnimationFrame } from "framer-motion";
useAnimationFrame((time, delta) => {
if (delta > 16.67) {
console.warn("Frame drop detected");
}
});
3. Forgetting Exit Animations
Always wrap with AnimatePresence for exit animations:
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Content
</motion.div>
)}
</AnimatePresence>
Results at Star Works
Implementing Framer Motion animations:
- 65% increase in user engagement
- 40% longer average session duration
- 50% more interactions with CTAs
- 95% positive user feedback on experience
Conclusion
Framer Motion empowers developers to create professional, performant animations with minimal code. By following best practices and using appropriate animation patterns, you can significantly enhance user experience.
Ready to bring your React application to life with stunning animations? Let's create something amazing together.
About the Author: Neha Verma is a Frontend Developer at Star Works specializing in React, animations, and interactive UI design. She has created award-winning interfaces for startups and enterprises.
Share this article
About Neha Verma
Frontend Developer at Star Works. Passionate about creating exceptional digital experiences.
View Full Profile →