CSS animations handle basic transitions. But orchestrated sequences, gesture-driven interactions, layout animations, and spring physics? You need a library. GSAP is powerful but imperative. react-spring has a learning curve. CSS keyframes can't do gesture-based animations.
What if React animations were as simple as adding animate props to your JSX?
import { motion } from "framer-motion";
function FadeIn({ children }) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
{children}
</motion.div>
);
}
That's Framer Motion. Declarative animations for React. Spring physics, gestures, layout animations, scroll-triggered effects — all with a clean API.
Core Animations
// Hover and tap
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
transition={{ type: "spring", stiffness: 400 }}
>
Click me
</motion.button>
// Drag
<motion.div
drag
dragConstraints={{ left: -100, right: 100, top: -100, bottom: 100 }}
dragElastic={0.2}
/>
// Scroll-triggered
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
viewport={{ once: true }}
/>
Layout Animations
function ExpandableCard({ isOpen }) {
return (
<motion.div layout style={{ borderRadius: 20 }}>
<motion.h2 layout>Title</motion.h2>
{isOpen && (
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
>
Expanded content appears here with smooth animation
</motion.p>
)}
</motion.div>
);
}
Just add layout prop. Framer Motion automatically animates between layouts — size, position, and all.
Staggered Lists
const container = { hidden: {}, show: { transition: { staggerChildren: 0.1 } } };
const item = { hidden: { opacity: 0, y: 20 }, show: { opacity: 1, y: 0 } };
function List({ items }) {
return (
<motion.ul variants={container} initial="hidden" animate="show">
{items.map(i => (
<motion.li key={i.id} variants={item}>{i.name}</motion.li>
))}
</motion.ul>
);
}
Exit Animations
import { AnimatePresence, motion } from "framer-motion";
function Notifications({ items }) {
return (
<AnimatePresence>
{items.map(item => (
<motion.div
key={item.id}
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
>
{item.message}
</motion.div>
))}
</AnimatePresence>
);
}
AnimatePresence animates components OUT of the DOM — something CSS can't do.
When to Choose Framer Motion
Choose it for: gesture-driven UI, layout animations, page transitions, complex orchestrated sequences.
Skip it for: simple hover effects (CSS is fine), non-React projects, SSR-heavy pages (adds 30KB).
Start here: motion.dev
Need custom data extraction, scraping, or automation? I build tools that collect and process data at scale — 78 actors on Apify Store and 265+ open-source repos. Email me: Spinov001@gmail.com | My Apify Actors
Top comments (0)