Why Framer Motion?
Framer Motion has largely won the "animation wars" in the React ecosystem by prioritizing Developer Experience (DX) above all else. While other libraries require understanding physics or complex imperative logic, Framer Motion allows you to describe what you want to happen, not how to calculate the frames.
- Declarative Layout Animations: The
layoutprop is arguably its strongest feature. It automatically handles FLIP (First, Last, Invert, Play) calculations, allowing elements to smoothly animate to new positions when the DOM layout changes (e.g., reordering a list). - Variant Orchestration: Managing complex sequences where parent and child animations need to be synchronized (like staggering a list of items) is trivial using
variants. - Production-Ready Gestures: It includes a complete gesture system supporting drag, pan, hover, and tap with physics-based constraints, eliminating the need for separate gesture libraries.
- Shared Layout Transitions: The
<LayoutGroup>andlayoutIdprops enable distinct components to morph into one another, creating seamless "hero" transitions between routes or UI states. - Accessibility First: It respects the user's
prefers-reduced-motionsetting automatically, ensuring your app remains accessible without extra configuration.
Code Snippet
This example demonstrates Variants, a powerful pattern to orchestrate animations across a component tree. Notice how the parent controls the timing of its children without passing props manually.
import { motion } from "framer-motion";
// 1. Define animation states (Variants)
const listVariants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
when: "beforeChildren", // Wait for this to finish before starting children
staggerChildren: 0.1 // Delay each child by 0.1s
}
}
};
const itemVariants = {
hidden: { x: -20, opacity: 0 },
visible: { x: 0, opacity: 1 }
};
export const Menu = ({ items }) => (
// 2. Bind variants to the motion component
<motion.ul
initial="hidden"
animate="visible"
variants={listVariants}
style={{ listStyle: "none" }}
>
{items.map((item) => (
// 3. Children automatically inherit "hidden" and "visible" states
<motion.li key={item.id} variants={itemVariants}>
{item.label}
</motion.li>
))}
</motion.ul>
);
The beauty of this pattern is that motion.li doesn't need to know when to animate. The parent motion.ul propagates the animate="visible" state down the tree, and the staggerChildren property handles the delay automatically.
Pros and Cons
No library is perfect; understanding the trade-offs is key to selecting the right tool.
Pros
- Unmatched DX: The API is incredibly intuitive for React developers. It feels like standard CSS-in-JS but with superpowers.
- Layout Magic: Animating between DOM states (width/height/position changes) is often as simple as adding
layout. - Documentation: The documentation is world-class, featuring interactive sandboxes for almost every feature.
- Ecosystem Integration: Works flawlessly with popular CSS-in-JS libraries and Tailwind (via standard
classNameorstyleprops).
Cons
- Bundle Size: It is heavier than alternatives like
react-springor vanilla CSS. WhileLazyMotionexists to reduce initial load, the full library is substantial (~30kb gzipped). - Performance Overhead: For simple interactions (like hover states), it adds JavaScript overhead where CSS transitions would be virtually free.
- Runtime Cost: Heavy usage of layout animations on complex trees can cause frame drops on lower-end devices if not optimized (e.g., using
will-change).
Comparison with Other Animation Libraries
The table below outlines the positioning differences between Framer Motion and other popular animation libraries to help you make an informed decision:
| Library | Design Philosophy | Best For | Pain Points |
|---|---|---|---|
| Framer Motion | Declarative & CSS-like Focuses on defining states (start/end) and letting the library handle the transition logic. | Complex UI & Layouts Applications needing shared layout transitions, gesture handling, and orchestrated sequences. | Bundle Size Can be overkill for simple micro-interactions; heavier initial load. |
| React Spring | Physics-based Simulates real-world physics (mass, tension, friction) rather than time-based durations. | Fluid Interactions UIs that need to feel "natural" and interruptible (e.g., drag-and-throw interfaces). | Learning Curve The API is more imperative and mathematical; harder to orchestrate complex sequences. |
| Anime.js | Imperative & Agnostic A powerful JavaScript animation engine that isn't tied to React's render cycle. | Creative WebGL/Canvas Complex timelines or animations outside the React component tree (e.g., HTML5 Canvas). | React Integration Requires manual useEffect refs management; fights against React's declarative nature. |
Verdict: When to Adopt
Framer Motion is the default recommendation for 95% of React applications. If you are building a product dashboard, a marketing site, or a mobile-web app, its layout capabilities and ease of use outweigh the bundle size cost.
Choose React Spring only if you are building a highly interactive, physics-driven experience (like a card-swiping game) where "natural" momentum is critical. Choose Anime.js only if you are orchestrating complex timelines that are largely independent of React state.