framer-motion, gsap, popmotion, and react-spring are leading libraries for adding movement and interactivity to web applications. framer-motion is built specifically for React, offering a declarative API that integrates tightly with component lifecycles and layout changes. gsap (GreenSock Animation Platform) is a robust, imperative engine that works with any JavaScript environment, known for precise timeline control and high performance. react-spring focuses on physics-based animations, using spring dynamics instead of fixed durations to create natural motion. popmotion serves as a functional, reactive animation engine that powers other libraries but can also be used directly for custom, low-level animation logic.
Animation brings interfaces to life, but choosing the right engine affects everything from code structure to performance. framer-motion, gsap, popmotion, and react-spring all solve the same problem — moving elements on a screen — but they approach it from different angles. Let's compare how they handle real-world engineering tasks.
The core difference lies in how you tell the library what to do.
framer-motion uses a declarative approach. You describe the end state in props, and the library handles the transition. This fits naturally into React's mental model.
// framer-motion: Declarative state
import { motion } from "framer-motion";
function Box() {
const [isOpen, setIsOpen] = useState(false);
return (
<motion.div
animate={{ scale: isOpen ? 1.2 : 1 }}
onClick={() => setIsOpen(!isOpen)}
/>
);
}
gsap uses an imperative approach. You select elements and command them to move using methods like .to() or .from(). This gives you direct control over execution.
// gsap: Imperative command
import { gsap } from "gsap";
import { useGSAP } from "@gsap/react";
function Box() {
const { contextSafe } = useGSAP();
const handleClick = contextSafe(() => {
gsap.to(".box", { scale: 1.2, duration: 0.3 });
});
return <div className="box" onClick={handleClick} />;
}
react-spring uses a physics-based model. Instead of durations, you configure tension and friction. The animation calculates values based on physical forces.
// react-spring: Physics configuration
import { useSpring, animated } from "@react-spring/web";
function Box() {
const [isOpen, setIsOpen] = useState(false);
const props = useSpring({
from: { scale: 1 },
to: { scale: isOpen ? 1.2 : 1 },
config: { tension: 200, friction: 20 }
});
return <animated.div style={props} onClick={() => setIsOpen(!isOpen)} />;
}
popmotion uses a functional reactive model. You create values and subscribe to updates, often wiring them manually to DOM elements.
// popmotion: Functional value stream
import { animate, value } from "popmotion";
const scale = value(1, v => element.style.scale = v);
function startAnimation() {
animate({
from: 1,
to: 1.2,
onUpdate: scale.set
});
}
Complex animations often require steps to run one after another.
framer-motion handles sequences via the transition prop or by chaining variants. It works well for component-based sequences but can get verbose for long timelines.
// framer-motion: Variants for sequencing
const variants = {
hidden: { opacity: 0, x: -100 },
visible: {
opacity: 1,
x: 0,
transition: { delay: 0.5 }
}
};
<motion.div variants={variants} animate="visible" />;
gsap shines here with its Timeline feature. You can stack animations precisely, overlap them, and control the whole sequence as one object.
// gsap: Timeline control
const tl = gsap.timeline();
tl.to(".box1", { x: 100 })
.to(".box2", { x: 100 }, "<0.2"); // Start 0.2s before previous ends
react-spring does not have a built-in timeline. You manage sequencing by chaining springs or using useChain, which references multiple spring refs.
// react-spring: useChain for sequencing
const springRef1 = useSpringRef();
const springRef2 = useSpringRef();
// ... configure springs with refs ...
useChain([springRef1, springRef2], [0, 0.5]);
popmotion requires manual chaining. You use callbacks like onComplete to trigger the next animation function.
// popmotion: Manual chaining
animate({
from: 0,
to: 100,
onComplete: () => {
animate({ from: 100, to: 200 });
}
});
How the library plays with React's render cycle is critical for performance.
framer-motion is built for React. It minimizes re-renders by handling animations on a separate thread where possible and uses React state only for high-level logic.
// framer-motion: Optimized for React
// No manual refs needed for basic animations
<motion.div animate={{ x: 100 }} />
gsap traditionally manipulates the DOM directly, bypassing React. With the new useGSAP hook, it cleans up safely to avoid memory leaks, but you still manage refs explicitly.
// gsap: Direct DOM manipulation
const boxRef = useRef(null);
useEffect(() => {
gsap.to(boxRef.current, { x: 100 });
}, []);
react-spring interpolates values outside the React render loop. It updates styles directly on the animated component, preventing unnecessary React commits.
// react-spring: Interpolation outside render loop
const { x } = useSpring({ x: 100 });
return <animated.div style={{ transform: x.to(v => `translateX(${v}px)`) }} />;
popmotion leaves integration up to you. In React, you typically combine it with useMotionValue (from framer-motion) or manage your own refs and requestAnimationFrame loops.
// popmotion: Manual React integration
useEffect(() => {
const controls = animate({
from: 0,
to: 100,
onUpdate: (v) => ref.current.style.left = v + "px"
});
return controls.stop;
}, []);
Handling user input and layout shifts is a common pain point.
framer-motion has built-in gesture props (whileHover, whileTap, drag). It also handles layout animations automatically with the layout prop.
// framer-motion: Built-in gestures and layout
<motion.div
whileHover={{ scale: 1.1 }}
layout
onClick={() => setIsOpen(!isOpen)}
/>
gsap requires external plugins or manual event listeners for gestures. Layout changes must be measured and animated manually using Flip plugin or similar logic.
// gsap: Manual gesture handling
div.addEventListener("mouseenter", () => {
gsap.to(div, { scale: 1.1 });
});
react-spring provides a useGesture hook (often via @use-gesture/react) to handle input. Layout animations require manual measurement of start and end positions.
// react-spring: External gesture hook
const bind = useDrag(({ movement: [mx] }) => ({ x: mx }));
return <animated.div {...bind()} style={{ x }} />;
popmotion provides input trackers like pointer but requires you to wire them to output values manually. Layout logic is entirely custom.
// popmotion: Custom input wiring
const x = value(0);
pointer(({ point: { x } }) => x.set(x));
| Feature | framer-motion | gsap | react-spring | popmotion |
|---|---|---|---|---|
| Primary Model | Declarative (React) | Imperative (Timeline) | Physics-based | Functional Reactive |
| React Integration | ⭐⭐⭐⭐⭐ (Native) | ⭐⭐⭐⭐ (via hooks) | ⭐⭐⭐⭐⭐ (Native) | ⭐⭐ (Manual) |
| Timeline Control | Variants / Transitions | ⭐⭐⭐⭐⭐ (Robust Timeline) | useChain | Manual Chaining |
| Gestures | Built-in (whileHover) | Manual / Plugins | External Hook | Manual Wiring |
| Layout Animation | Automatic (layout prop) | Manual / Flip Plugin | Manual Measurement | Custom Logic |
| Best Use Case | UI Interactions | Complex Sequences | Physics / Drag | Custom Engines |
framer-motion is the default choice for modern React apps. It removes the boilerplate of refs and effects, letting you animate components as if they were standard HTML. It handles the hard parts — like layout shifts and gestures — out of the box.
gsap remains the king of complex, time-based animation. If you are building a marketing site with scroll-triggered sequences or need to coordinate dozens of elements precisely, nothing beats its timeline API. It works everywhere, not just in React.
react-spring is the specialist for natural motion. If your app feels too robotic with standard transitions, springs add weight and momentum. It is excellent for draggables and interactive physics.
popmotion is the engine room. Unless you are building your own animation library or need to animate non-DOM targets (like Three.js or Canvas) with a functional approach, you will likely use Framer Motion or React Spring instead, as they build on these concepts for you.
Final Thought: For most React developers, start with framer-motion. It offers the best balance of power and simplicity. Reach for gsap when you need timeline precision, and react-spring when you need physics. Use popmotion only if you have very specific low-level requirements that the others cannot meet.
Choose framer-motion if you are building a React application and want a declarative API that feels like native React. It is ideal for UI interactions, layout animations, and gesture handling without needing to manage refs or effects manually. It balances ease of use with powerful features like shared layout transitions.
Choose gsap if you need precise control over complex timelines, sequenced animations, or if you are working outside of React (vanilla JS or other frameworks). It is the industry standard for award-winning web experiences where performance and fine-grained control over every millisecond matter.
Choose popmotion if you need a low-level, functional animation engine to build custom hooks or integrate with non-DOM targets like Canvas or WebGL. It is best for developers who want to construct their own animation abstractions rather than using a pre-built component library.
Choose react-spring if your design requires physics-based motion, such as draggable items with momentum or natural decay. It is suitable for projects where fixed-duration transitions feel too rigid and you want animations that respond dynamically to user input.
npm install motion
Motion is available for React, JavaScript and Vue.
import { motion } from "motion/react"
function Component() {
return <motion.div animate={{ x: 100 }} />
}
Get started with Motion for React.
Note: Framer Motion is now Motion. Import from motion/react instead of framer-motion.
import { animate } from "motion"
animate("#box", { x: 100 })
Get started with JavaScript.
<script>
import { motion } from "motion-v"
</script>
<template> <motion.div :animate={{ x: 100 }} /> </template>
Get started with Motion for Vue.
Browse 330+ official examples, with copy-paste code that'll level-up your animations whether you're a beginner or an expert.
Over 100 examples come with a full step-by-step tutorial.
A one-time payment, lifetime-updates membership:
Motion is sustainable thanks to the kind support of its sponsors.
Motion powers the animations for all websites built with Framer, the web builder for creative pros. The Motion website itself is built on Framer, for its delightful canvas-based editing and powerful CMS features.
Motion drives the animations on the Cursor homepage, and is working with Cursor to bring powerful AI workflows to the Motion examples and docs.