BROWSE
GITHUB
This post was inspired by this video. GSAP and Motion (formerly Framer Motion) are both powerful libraries for creating animations on the web. So I asked myself which one fits my needs best as a React developer.
First of all, I need to mention that simple CSS transitions shouldn’t be ignored. Many animations can be implemented perfectly well with plain CSS or, for example, Tailwind utilities.
However, once interactions become more dynamic or complex, libraries like GSAP or Motion start to make more sense.
Those who worked with Motion before already know that it is far more React-oriented. Basically it is built for it, so why would someone choose something else? I think the answer lies in the approach and the limits each library has.
Declarative or Imperative?
The first thing that stands out when working with Motion is how naturally it fits into the React mental model.
Instead of instructing the browser how to animate something step by step, you describe states. Motion handles the transition between them. In other words, animation becomes just another part of the component’s behavior.
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}>
</motion.div>
There’s something satisfying about this approach. The animation reads almost like component state: the element starts here, ends there, and Motion takes care of everything in between. It aligns well with how React itself works: it’s declarative, predictable, and component-oriented.
GSAP, on the other hand, is different from the very beginning. Instead of describing states, you describe actions.
gsap.to(".box", {
y: 100,
duration: 0.5,
});
Or if you want to choreograph multiple steps:
const tl = gsap.timeline();
tl.to(".box", { x: 100 });
tl.to(".box", { rotation: 90 });
This approach is more imperative. You tell the animation engine exactly what to do and when. At first glance it may look slightly more verbose and boilerplate, but it opens the door to a level of control that declarative animation often struggles to provide.
The difference between the two libraries is less about syntax and more about how you think about animation. Motion focuses on declaring visual states and letting the library handle transitions between them. GSAP focuses on building a sequence of animation instructions, so you can describe what happens, when it happens, and how elements move over time.
Motion is React’s #1 Pick
For most React tasks, Motion feels like the more comfortable and natural choice. Many interfaces need small, contextual animations rather than complex choreography.
Buttons scale slightly when pressed. Modals fade in. Cards rearrange smoothly when their order changes. These interactions are tied to the lifecycle of React components, and Motion integrates directly with that lifecycle.
One particularly elegant feature is layout animation. When elements change position in the layout, Motion can animate the transition automatically:
<motion.div layout />
Without writing any animation logic, items can smoothly shift and resize when the layout changes. This makes features like sortable lists or dynamic grids feel significantly more polished.
Motion also provides built-in gesture handling: dragging, tapping, and hovering can be expressed directly through the component API, which reduces the need for additional libraries or event logic.
In many product interfaces, that’s exactly the level of animation complexity you need.
GSAP — a powerhouse engine, and also free!
Once you have multiple elements, carefully timed sequences, synchronized transitions, the declarative approach may hit a dead end. What works beautifully for a single component becomes harder when orchestrating a whole scene.
This is where GSAP tends to shine.
Because it is fundamentally an animation engine rather than a UI library, it gives you full control over timelines. Animations can overlap, stagger, and synchronize with precise timing. Instead of describing states, you build a sequence of events.
That level of control becomes especially useful for storytelling experiences: animated landing pages, complex hero sections, or scroll-driven effects where progress directly maps to animation state.
GSAP’s ScrollTrigger plugin is a good example of this capability. It allows animations to react to scroll position in a very controlled way, enabling effects that would be difficult or fragile with simpler tools.
For many marketing or interactive design projects, that kind of control becomes essential.
React Integration
One thing that initially worried me about GSAP was how it would behave inside React. Imperative code and component lifecycles do not always mix well.
In practice, though, the integration is manageable. Animations are usually created inside useEffect or useLayoutEffect, and GSAP provides helpers that make cleanup straightforward. The code ends up slightly more manual than Motion, but not dramatically so.
import { useLayoutEffect, useRef } from "react";
import gsap from "gsap";
function Box() {
const ref = useRef(null);
useLayoutEffect(() => {
const ctx = gsap.context(() => {
gsap.to(".box", {
y: 100,
duration: 0.5,
});
}, ref);
return () => ctx.revert();
}, []);
return (
<div ref={ref}>
<div className="box" />
</div>
);
}
gsap.context() is useful because it scopes selectors and automatically cleans up animations when the component unmounts.
The difference is mostly stylistic: Motion expresses animation as component state, while GSAP expresses it as an imperative animation command.
import { motion } from "motion/react";
function Card() {
return (
{/* prettier-ignore */}
<motion.div
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ duration: 0.3 }}>
Card
</motion.div>
);
}
import { useLayoutEffect, useRef } from "react";
import gsap from "gsap";
function Card() {
const ref = useRef(null);
useLayoutEffect(() => {
gsap.from(ref.current, {
scale: 0.9,
opacity: 0,
duration: 0.3,
});
}, []);
return <div ref={ref}>Card</div>;
}
Choosing Between
After working with both tools, I’ve found a simple rule of thumb.
When animation is part of the interface behavior, Motion tends to be the most comfortable choice. Its declarative approach matches React’s design philosophy, and it handles common UI patterns elegantly.
When animation becomes the core experience itself, GSAP often provides the flexibility needed to build it (timelines and ScrollTrigger rule).
In fact, many projects can even combine both approaches. Motion handles component-level interactions, while GSAP powers more elaborate visual sequences. So there is no competition for me here, both are good, just for slightly different occasions.