Comparing the best React animation libraries for 2026
Editor’s note: This article was updated by Chizaram Ken in January 2026 to remove outdated libraries and add a hands-on performance and bundle-size benchmark comparing popular animation stacks (including native CSS and Tailwind CSS Motion).
For a React frontend developer, implementing animations on webpages is an integral part of your daily work, from animating text or images to complex 3D animations. Animation can help improve the overall user experience of a React application. But with so many libraries available, it’s hard to know which one to choose. Your animations might be hurting your performance more than you think.
In this article, we’ll compare the top seven React animation libraries and evaluate each for popularity, developer experience, readability, documentation, and bundle size to help you choose the right library for your next React project. We compared them based on bundle size, developer experience (DX), and performance.
Also, at the end of this article, we tested nine popular approaches to find the best animation stack for 2026. This benchmark includes a couple of CSS-first approaches (native CSS and TailwindCSS Motion) alongside the libraries, since they’re common production alternatives for UI animation.
A quick snapshot of all the libraries we explored:
- React Spring: Physics-based animations with spring dynamics (29k stars, 788k weekly downloads)
- Motion (formerly Framer Motion): Declarative animations with gestures and scroll support (30.7k stars, 3.6M weekly downloads)
- React Transition Group: Mounting/unmounting transitions; no longer maintained (10.3k stars, 20M weekly downloads)
- React Move: D3.js-powered animations for data visualization (6.6k stars, 88.6k weekly downloads)
- Remotion: Create videos programmatically with React (25.3k stars, 169.9k weekly downloads)
- Anime.js: Lightweight JavaScript engine for CSS, SVG, and timelines (66k stars, 319k weekly downloads)
- GreenSock (GSAP): Professional scroll effects and timeline animations (23.6k stars, 1.47M weekly downloads)
- TailwindCSS Motion: Utility-first CSS animations for Tailwind projects (3k stars, 33k weekly downloads)
- Approaches we also benchmarked: Native CSS
Let’s get into it!
React Spring
React Spring is a modern animation library that is based on spring physics. It’s highly flexible and covers most animations needed for a user interface. Drawing inspiration from React Motion, React Spring inherits ease of use, while also borrowing some powerful performance attributes from React-Animated-CSS.
To see React Spring in action, install the library by using one of the commands below:
npm i @react-spring/web yarn add @react-spring/web
Next, add the code below to create an animated Hello React Spring text:
import { useSpring, animated } from "@react-spring/web";
import { useState } from "react";
function ReactSpring() {
// State to keep track of the animation toggle
const [state, toggle] = useState(true);
// Define the animation using the useSpring hook
const { x } = useSpring({
from: { x: 0 }, // Starting value of the animated property
x: state ? 1 : 0, // Ending value of the animated property based on the state
config: { duration: 1000 }, // Configuration for the animation, specifying the duration
});
// Return the animated component
return (
<div onClick={() => toggle(!state)}>
<animated.div
style={{
opacity: x.to({ range: [0, 1], output: [0.3, 1] }),
transform: x
.to({
range: [0, 0.25, 0.35, 0.45, 0.55, 0.65, 0.75, 1],
output: [1, 0.97, 0.9, 1.1, 0.9, 1.1, 1.03, 1],
})
.to((x) => `scale(${x})`),
}}
>
Hello React Spring
</animated.div>
</div>
);
}
export default ReactSpring;
When you click on the div, the above code animates both the opacity and scale of the contents. To understand better, let’s break it down step by step.
Within our component, state is used to toggle the animation. When the div is clicked, the toggle function is called, changing state from true to false, or vice versa. This triggers the animation due to the change in the x value provided to the useSpring Hook.
The useSpring Hook is used to define the animation:
from: Defines the starting values of the animated properties. In this case, we are animating the x property, starting from 0.x: This is the property that will be animated. It is dependent on the state variable, so when state is true, x will be 1, and when state is false, x will be 0.config: Specifies the configuration for the animation. In this example, the animation will have a duration of1000ms.
The animated.div component is used to wrap the contents that we want to animate. It uses the animated wrapper from React Spring, allowing it to animate the styles applied to it. The animation itself is controlled by the style prop of the animated.div component. The style object is defined using the x value obtained from the useSpring Hook.
Two CSS properties are animated in this example:
opacity: The opacity of the content is interpolated from0.3to1based on thexvalue.transform: The scale of the content is interpolated based on thexvalue. Thex.tofunction maps specific input ranges to corresponding output values, effectively defining how the scale changes during the animation.
React Spring pros
- Ease of use: React Spring has a straightforward API and integrates well with React components using Hooks and functional components.
- Performance: Physics-based animation creates smooth animations with a high frame rate.
- Interpolation: Supports interpolation, which allows us to smoothly transition values between different states.
- Popularity: 29k stars on GitHub and 788k weekly downloads on npm. Used by companies and startups such as Aragon, CodeSandbox, Next.js, and many others.
- Documentation and support: Well-documented with examples, and available to support the community via GitHub, Twitter, and Discord.
React Spring cons
- Bundle size: Physics-based animations, complex transitions, and a more extensive feature set make it heavier compared to other animation libraries.
- Physics-based approach: While the physics-based approach is a strength, it may not be suitable for every animation. Some animations might require precise control, which could be challenging to achieve with spring physics alone.
- Not suitable for all projects: Considering the extensive list of features and bundle size, it might be overkill for projects that require basic animations.
Motion (formerly Framer Motion)
Motion is a popular React animation library that makes creating animations easy. It boasts a simplified API that abstracts the complexities behind animations and allows developers to create animations with ease. Even better: it has support for server-side rendering, gestures, and CSS variables.
To install Motion, run one of the following two commands in your terminal:
yarn add framer-motion npm install framer-motion
Next, add the following code block to add cool, yet simple animation to a square-shaped element:
import { useState } from "react";
import { motion } from "motion/react";
import "./framer-motion.css"; // Import any CSS file if required
function FramerMotion() {
// State to keep track of the animation toggle
const [isActive, setIsActive] = useState(false);
// Return the animated component
return (
<motion.div
className="box"
onClick={() => setIsActive(!isActive)} // Toggle the 'isActive' state on click
animate={{
rotate: isActive ? [0, 90, 180, 270] : [270, 180, 90, 0], // Rotate the element based on 'isActive' state
borderRadius: isActive ? [0, 20, 50] : [50, 20, 0], // Change border radius based on 'isActive' state
}}
></motion.div>
);
}
export default FramerMotion;
When you click on the div, the above code toggles the animation, rotating it in a circular motion and changing its border radius, creating a smooth and visually appealing effect.
Within our component, isActive is used to toggle the animation. When the div is clicked, the onClick event handler is called, which toggles the value of isActive between true and false. The animation is controlled using the animate prop of the motion.div component from Motion. The animate prop takes an object with properties that describe the animations to be applied to the element.
Then, the rotate property animates the rotation of the element. When isActive is true, the element will rotate from zero degrees to 90 degrees, then to 180 degrees, and finally to 270 degrees. When isActive is false, the element will rotate in the reverse order, from 270 degrees to 180 degrees, then to 90 degrees, and finally back to zero degrees.
The borderRadius property animates the border radius of the element. When isActive is true, the border radius will change from zero to 20 and then to 50. When isActive is false, the border radius will change in the reverse order, from 50 to 20, and finally back to zero.
Motion pros
- Ease of use: Motion has a developer-friendly API and comes with declarative syntax, making it simple to define animations.
- Performance: Hardware-accelerated animations resulting in smooth animations even on less powerful devices.
- Rich animation features: The library offers a wide range of animations, including basic animations (e.g., opacity, scale) and more advanced ones like physics-based animations and keyframes.
- Accessibility: The library aims to be accessible by default. It takes into consideration factors like reducedMotion, which can scale down or entirely disable motion, making the experience more accessible for users who may be sensitive to motion.
- Bundle size: Uses LazyMotion to reduce bundle size by synchronously or asynchronously loading some, or all, of the
motioncomponent’s features. - Popularity: 30.7k stars on GitHub and 3.6m weekly downloads on npm.
- Documentation and support: Well-documented with examples and available to support the community via GitHub, Twitter, and Discord.
Motion cons
- Not ideal for all projects: For basic animations, using a lightweight animation library might be more suitable, as Motion could be overkill in such cases.
- Limited browser support: Some older browsers may not fully support the latest features and APIs used by Motion.
React Transition Group
React Transition Group is a library that provides a set of components to help manage the animation of elements when they are added to or removed from the React component tree. It is commonly used in combination with React for creating smooth and seamless transitions for elements as they enter or exit the DOM.
Note: This library is no longer maintained by the authors.
To install the library, run one of the following commands in your terminal:
npm i react-transition-group yarn add react-transition-group
Next, add this code block to create a basic fade animation using React Transition Group:
import { useState } from "react";
import { Transition } from "react-transition-group";
function ReactTransitionGroup() {
const [show, setShow] = useState(true);
const handleToggle = () => {
setShow(!show);
};
const duration = 300; // Animation duration in milliseconds
// Custom styles for the enter animation
const transitionStylesEnter = {
entering: { opacity: 0 },
entered: { opacity: 1 },
};
// Custom styles for the exit animation
const transitionStylesExit = {
exiting: { opacity: 1 },
exited: { opacity: 0 },
};
return (
<div>
<button onClick={handleToggle}>Toggle Fade</button>
<Transition in={show} timeout={duration}>
{(state) => (
<div
className="fade-element"
style={{
...transitionStylesEnter[state],
...transitionStylesExit[state],
transition: `opacity ${duration}ms`, // Apply the custom animation duration
}}
>
Hello, this is a fade animation!
</div>
)}
</Transition>
</div>
);
}
export default ReactTransitionGroup;
When you click the "Toggle Fade" button, the element will fade in or fade out smoothly, depending on the current value of the show state. The Transition component handles the custom animation logic using the provided styles and lifecycle Hooks. To better understand, let’s break it down step by step.
Within our component, we define show to control the visibility of the fading element. The handleToggle function toggles the show state when the "Toggle Fade" button is clicked. Then, we set the duration variable to control the animation duration for both enter and exit transitions. In this example, the duration is set to 300 milliseconds.
Next, we define two objects, transitionStylesEnter and transitionStylesExit, to specify the custom styles for the enter and exit animations, respectively. These styles are applied based on the current state of the transition using the state argument provided by the render prop function inside the Transition component.
Inside the return statement, we use the Transition component from React Transition Group.
The Transition component takes two main props: in and timeout. The in props is a Boolean value that determines whether the element should be shown or hidden. In this case, it depends on the value of the show state. Meanwhile, the timeout prop displays the duration of the animation in milliseconds. In this example, we set it to the value of the duration variable (300 milliseconds).
The Transition component uses a render prop pattern, where the child function receives the current state of the transition as an argument. The state value can be one of the following strings: "entering", "entered", "exiting", or "exited". These states represent the different stages of the enter and exit transitions. Then, inside the render prop function, we render the fading element (<div className="fade-element">) with inline styles.
The state argument is used to apply the corresponding custom styles that we defined earlier for the enter and exit animations. Finally, the transition property is set to apply the custom animation duration for the opacity transition.
React Transition Group pros
- Flexible animation management: React Transition Group offers a versatile set of components and lifecycle hooks to manage animations during the mounting, unmounting, and state changes of components. It allows for more granular control over animations compared to CSS transitions alone.
- CSS transition support: The library works seamlessly with CSS transitions. It enables you to apply CSS classes at specific stages of the animation lifecycle, allowing you to leverage existing CSS animations and transitions.
- Small bundle size: React Transition Group is designed with a focus on lightweight transitions. It generally has a smaller bundle size compared to more comprehensive animation libraries.
- Popularity: 10.3k stars on GitHub and 20M weekly downloads on npm.
React Transition Group cons
- Learning curve: The API can be complex, especially for developers new to animation or those who are not yet familiar with React’s lifecycle methods.
- Limited use case: The library is more appropriate for projects that primarily involve transitions during mounting, unmounting, and state changes.
- Documentation: The docs are not very beginner-friendly, as they are just plain text with no demos.
React Transition Group also comes with support for TypeScript, which can be installed using the command: npm i @types/react-transition-group.
React Move
React Move is a library designed for creating beautiful and data-driven animations by leveraging the animation capabilities of D3.js. It was designed to support TypeScript out of the box and also supports custom tweening functions.
Tweening is short for “inbetweening,” the process of generating images for frame-by-frame animation between keyframes. React Move also features lifecycle events in its transitions, as well as allowing developers to pass on custom tweens in their animations.
To install React Move, run one of the following two commands in your terminal:
npm install react-move yarn add react-move
Next, add this code block to create animated bars using React Move:
import { useState, useEffect } from "react";
import { Animate } from "react-move";
import "./AnimatedBars.css"; // Import the CSS file with the styles
// Define an array of colors for the bars
const colors = ["#236997", "#52aaeb", "#a75e07", "#f4a22d", "#f95b3aff"];
// Define an array of data values for the bars' heights
const data = [10, 26, 18, 14, 32];
function AnimatedBars() {
// Initialize the state for the index of the data array
const [index, setIndex] = useState(0);
// Use the useEffect hook to set up the interval for updating the index
useEffect(() => {
// Set an interval to update the index every 2 seconds (2000 milliseconds)
const intervalId = setInterval(() => {
setIndex((prevIndex) => (prevIndex + 1) % data.length);
}, 2000);
// Clean up the interval when the component unmounts
return () => {
clearInterval(intervalId);
};
}, []);
// Render the animated bars using react-move
return (
<div className="App">
{/* Create an SVG element to draw the bars */}
<svg version="1.1" viewBox="0 0 240 135" width="100vw">
{/* Group the bars under a 'g' element */}
<g>
{/* Loop through the data array to create animated bars */}
{data.map((value, i) => (
// Use the Animate component to animate the bars' heights
<Animate
key={i}
start={{ height: 0 }} // Initial state of the animation (height starts from 0)
enter={{ height: [value], timing: { duration: 500 } }} // Target state when a new data value is added
update={{
height: [data[(i + index) % data.length]], // Target state when the index state changes
timing: { duration: 500 },
}}
>
{/* Render a rect element for each bar */}
{(state) => (
<rect
x={60 + 30 * i} // X position of the bar
y={115 - state.height} // Y position of the bar (height changes based on state)
width={24} // Width of the bar
height={state.height} // Height of the bar (changes based on state)
fill={colors[i % colors.length]} // Fill color of the bar (based on the colors array)
/>
)}
</Animate>
))}
</g>
</svg>
</div>
);
}
export default AnimatedBars;
The code above renders a set of animated bars using the React Move library, with changing height and color every two seconds. To understand better, let’s break it down.
First, we define an array of colors and data to be used to style the bars and determine their heights. Meanwhile, the index state variable will be used to update the bars. Then, the useEffect() Hook sets up an interval to update the index state every two seconds. This will trigger the animation and update the bars based on the data array.
To render the animated bars, we group the bars using a <g> element inside the <svg> element. Then, we loop through the data array and create an Animate component for each value. For each Animate component, we specify the start, enter, and update configurations for the animation:
start: The initial state of the animation. The height starts from zero enter: The target state of the animation when a new data value is added. The height is set to the corresponding value from the data array update: The target state of the animation when the index state changes. Height is updated to the corresponding value from the data array using the index state
Then, inside the Animate component, we define the animation and render a <rect> element for each data value:
<rect> elements represent the bars x, y, width, and fill attributes are set to style each bar based on its index and data value
React Move pros
- D3-based animation engine: React move uses D3.js, a widely used JavaScript library for data visualization, to handle animations. D3.js provides a rich set of features for animating elements, including transitions, interpolations, and easing functions, which allows React Move to offer a rich set of animations.
- Support for SVG and DOM elements: Capable of animating both SVG elements and DOM elements.
- Flexibility: Offers a wide range of animation options, including positional animations, color transitions, and more. It allows you to create complex animations with ease.
- Popularity: 6.6k stars on GitHub and 88.6K weekly downloads on npm.
React Move cons
- Learning curve: If new to animations or React, the learning curve might be steep. Understanding how to define animations with the correct syntax and usage can take some time.
- Animation complexity: Creating complex animations may require a deep understanding of the library’s features and can lead to more complex code.
- Bundle size: Not so lightweight when compared to other animation libraries like Motion.
- Documentation: Not very beginner-friendly, and has very limited examples.
- Community support: No Discord channel or Twitter presence.
Remotion
The Remotion React library was created in 2021 to create animations using HTML, CSS, and JavaScript. With Remotion, you can create videos as well as play and review them on your browser while using React components and compositions.
Remotion also allows developers to scale video animation and production using server-side rendering and parametrization. Parametrization is the process of finding equations of curves and surfaces using equations, which can be very useful in creating meshes for videos. You can initialize a new Remotion video by running either of the commands below in your terminal:
npm init video yarn create video
A video is a function of images over time. If you change content every frame, you’ll end up with an animation. To create one using Remotion, add the following lines of code:
import { useVideoConfig } from "remotion";
export const MyVideo = () => {
const { fps, durationInFrames, width, height } = useVideoConfig();
const opacity =frame >= 30 ? 1 : frame / 30;
return (
<div
style={{
flex: 1,
textAlign: "center",
fontSize: "9em",
opacity: opacity,
}}
>
This {width}px x {height}px video is {durationInFrames / fps} seconds long.
</div>
);
};
In the code above, we added animation properties using opacity. We also added a video animation using the useVideoConfig() Hook. When creating a video animation, we’d need the width, height, durationInFrames, and fps parameters in Remotion. You can read more about them here.
Remotion pros
- Programmatic video creation: Programmatically generating video frames with React components provides more control and flexibility over video content creation.
- Customization: With the power of React and CSS, Remotion allows for complex animations and visual effects, enabling developers to create engaging video content.
- Popularity: 25.3k stars on GitHub and 169.9K weekly downloads on npm.
- Documentation: Well-documented with examples.
- Community support: Available via GitHub, Twitter, and Discord.
Remotion cons
- Learning curve: Introduces video-specific concepts and APIs, which may require developers to learn new paradigms.
- Performance considerations: Generating video frames programmatically can be resource-intensive, and careful optimization is necessary to maintain smooth playback.
- Browser support: Video codecs and features used in Remotion may not be fully supported in all browsers, potentially limiting the cross-browser compatibility.
- Video editing limitations: While Remotion excels at programmatically creating video content, it may not provide the same level of advanced video editing features found in video editing software.
Anime.js
Anime.js is a lightweight JavaScript animation library with a simple, yet powerful API. It makes animating CSS properties, SVG elements, DOM attributes, and JavaScript objects a breeze.
To install Anime.js, run one of the following commands:
npm install animejs yarn add animejs
To create a cool SVG animation, add the lines of code below:
import { useEffect, useRef } from "react";
import anime from "animejs";
function Anime() {
const circleRef = useRef(null);
const animateCircle = () => {
// Get the DOM element to animate
const circle = circleRef.current;
// Use AnimeJS to create the animation
anime({
targets: circle,
cx: anime.random(10, 500), // Random x position between 10 and 500
cy: anime.random(10, 500), // Random y position between 10 and 500
duration: 2000, // Animation duration in milliseconds
easing: "easeInOutQuad", // Easing function for smooth animation
complete: animateCircle, // Repeat the animation infinitely
});
};
useEffect(() => {
animateCircle();
}, []);
return (
<svg width="500" height="500">
<circle ref={circleRef} r="30" fill="blue" />
</svg>
);
}
export default Anime;
In the code above, a circle moves randomly on the screen within the specified coordinate range and keeps looping infinitely. To understand better, let’s break it down.
Inside the component, a useRef Hook is used to create a reference called circleRef, which is used to get a reference to the SVG circle element that needs to be animated.
Meanwhile, the animateCircle function is responsible for animating the SVG circle element. Inside the function, the DOM element to animate is obtained using the circleRef.current. Then, Anime.js is used to create an animation using the anime() function. The targets option specifies the circle element to animate, and the cx and cy properties are animated with random values between 10 and 500, creating a random movement of the circle.
Then, the duration option sets the animation duration to 2000 milliseconds (two seconds), while the easing option specifies the easing function used for smooth animation. Finally, the complete option sets the animateCircle function to be called again when the animation is completed, creating an infinite loop. The r attribute sets the radius of the circle and the fill attribute sets the circle’s color.
Anime.js pros
- Versatile animation: This library supports a wide range of animations, including simple property animations like opacity, scale, and rotation, as well as more complex animations such as motion paths and sequences.
- Easing functions: Provides a variety of easing functions that allow developers to control the animation’s acceleration and deceleration, resulting in smooth and natural transitions.
- Timelines: Allows the creation of animation timelines, enabling the precise orchestration of multiple animations, delays, and sequencing for more synchronized effects.
- SVG animation: Seamlessly animates SVG elements and attributes, making it an excellent choice for creating interactive and dynamic SVG-based graphics.
- Chaining and callbacks: Anime.js can chain multiple animations together or attach callbacks to specific animation events, offering greater control over the animation flow.
- Documentation: Well-documented API references, guides, and examples, providing developers with all the necessary resources.
- Popularity: 66k stars on GitHub and 319K weekly downloads on npm.
Anime.js cons
- Performance: Generally performant for most animations, it may not be as optimized for heavy animations as some other libraries like Motion or React Spring.
- Smaller community: The Anime.js community is smaller compared to more popular libraries like Motion or GSAP.
- Limited interactivity: Lacks built-in interactivity features like drag-and-drop or click-to-animate, which are available in other animation libraries.
GreenSock (GSAP)
GSAP (GreenSock Animation Platform) is a robust and widely-used animation library for creating high-performance animations and interactive web experiences. It has been a popular choice among developers for many years. Known for its speed, flexibility, and smoothness, GSAP empowers developers to craft seamless animations with ease.
You can install GSAP with one of the commands below:
npm install gsap yarn add gsap
To create a cool scroll-triggered animation, add the following code:
import { useLayoutEffect, useRef } from 'react';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
export default function Scroll() {
const main = useRef();
useLayoutEffect(() => {
const ctx = gsap.context((self) => {
const boxes = self.selector('.box');
gsap.from(boxes, {
x: -500, // Start from left (-100px)
opacity: 0,
duration: 1,
stagger: 0.2, // Add stagger effect for sequential animation
scrollTrigger: {
trigger: main.current,
start: 'top 80%', // Adjust the start position for the effect
end: 'bottom 20%', // Adjust the end position for the effect
scrub: true,
},
});
}, main); // <- Scope!
return () => ctx.revert(); // <- Cleanup!
}, []);
return (
<div>
<section className="section flex-center column">
<h2>Scroll down to see the animation!!</h2>
</section>
<div className="section flex-center column" ref={main}>
<div className="box">Box 1</div>
<div className="box">Box 2</div>
<div className="box">Box 3</div>
<div className="box">Box 4</div>
<div className="box">Box 5</div>
</div>
<section className="section"></section>
</div>
);
}
On page scroll, the boxes will come from the left side of the screen with a stagger effect, and their opacity will increase, creating a smooth animation.
ScrollTrigger, which is imported from GSAP’s ScrollTrigger plugin, enables animations based on scroll positions.
gsap.registerPlugin(ScrollTrigger); is used to register the plugin with GSAP.
Then, const main = useRef(); creates a ref called main to reference the main container.
To set up the animation, gsap.context() is used to create a context for managing animations scoped to the main container. Then, gsap.from() is used to create the animation. The boxes element is selected using self.selector('.box'), and the animation targets each box element, starting from -500 units to the left (x: -500) and with opacity: 0.
The animation duration is set to 1 second, and stagger: 0.2 is used to add a stagger effect for sequential animations among the boxes. Then, scrollTrigger specifies the trigger options for the animation, which will be triggered based on the scroll position.
start: 'top 80%' specifies that the animation starts when the top of the main container is at 80% of the viewport’s height.
Meanwhile, end: 'bottom 20%' specifies that the animation ends when the bottom of the main container is at 20% of the viewport’s height.
Finally, scrub: true enables the scrub feature, which changes the animation progress based on scroll position. The animation will control the x position and opacity as specified in the animation setup.
GSAP pros
- Robust animation capabilities: Provides a powerful and flexible animation engine capable of handling complex animations with ease. It supports a wide range of properties, CSS attributes, SVG, and more.
- High performance: Optimized for performance and smooth animations.
- Cross-browser compatibility: Works consistently across different browsers, including older versions.
- Plugin system: Its plugin architecture allows easy integration of additional features and enhances its capabilities, such as scroll animations (ScrollTrigger) and physics-based animations (Physics2D).
- Timelines: Allows precise control over multiple animations, enabling synchronized and choreographed animations.
- Documentation: Offers comprehensive documentation with clear examples, making it easy for developers to understand and implement its features.
- Community support: Has a dedicated Help section with active forums to support developers.
- Popularity: 23.6k stars on GitHub and 1.47M weekly downloads on npm.
GSAP cons
- Learning curve: For newcomers, GSAP might have a steeper learning curve due to its extensive features and various animation methods.
- License limitations: GSAP is free for commercial use, but it requires attribution in certain cases, which could be a minor inconvenience for some projects.
- Bundle size: Compared to some other animation libraries, GSAP might be considered less lightweight. However, it compensates with its robustness and performance optimizations.
TailwindCSS Motion
Animating with some libraries makes you feel like you need an extra degree in designing. And we’re not designers; we’re developers, hence why this Tailwind animation plugin was created. It’s becoming a popular choice for simple, easy-going animations in React.
To install TailwindCSS Motion, run this in your projects:
npm i -D tailwindcss-motion
And go ahead to add this to your tailwind.config.js :
// tailwind.config.js
export default {
content: [...],
theme: {
extend: {...},
},
plugins: [require('tailwindcss-motion')],
};
Or you can use ESM:
import tailwindcssMotion from "tailwindcss-motion";
/** @type {import('tailwindcss').Config} */
export default {
content: [...],
theme: {
extend: {},
},
plugins: [tailwindcssMotion],
};
Now, let’s create a scroll-triggered card animation that fades in and slides up as you scroll:
import { useEffect, useRef, useState } from "react";
function ScrollCards() {
const [isVisible, setIsVisible] = useState({});
const cardRefs = useRef([]);
useEffect(() => {
const observers = cardRefs.current.map((card, index) => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible((prev) => ({ ...prev, [index]: true }));
}
},
{ threshold: 0.1 }
);
if (card) observer.observe(card);
return observer;
});
return () => observers.forEach((observer) => observer.disconnect());
}, []);
const cards = [
{ title: "Feature One", description: "Build faster with Tailwind utilities" },
{ title: "Feature Two", description: "Animate without writing keyframes" },
{ title: "Feature Three", description: "Pure CSS, zero JavaScript overhead" },
];
return (
<div className="min-h-screen bg-gray-50 py-20">
<div className="max-w-4xl mx-auto px-4 space-y-8">
{cards.map((card, index) => (
<div
key={index}
ref={(el) => (cardRefs.current[index] = el)}
className={`
bg-white p-8 rounded-lg shadow-lg
${
isVisible[index]
? "motion-preset-slide-up-lg motion-duration-700"
: "opacity-0 translate-y-12"
}
`}
style={{ transitionDelay: `${index * 150}ms` }}
>
<h3 className="text-2xl font-bold mb-3">{card.title}</h3>
<p className="text-gray-600">{card.description}</p>
</div>
))}
</div>
</div>
);
}
export default ScrollCards;
When you scroll down the page, the cards will appear one by one with a smooth slide-up and fade-in effect. The animation is staggered, creating a polished sequential reveal.
The code uses the Intersection Observer API to detect when each card enters the viewport. Once a card becomes visible, we apply Tailwind CSS Motion’s motion-preset-slide-up-lg utility class, which handles the entire animation with pure CSS. The motion-duration-700 class sets the animation duration to 700 milliseconds.
The beauty of this approach is in its simplicity. Instead of defining custom @keyframes or importing heavy JavaScript libraries, we’re using utility classes that compile down to optimized CSS. The motion-preset-slide-up-lg preset combines translation, opacity, and timing into a single class.
The transitionDelay inline style creates the stagger effect, making each subsequent card animate slightly after the previous one. This adds a professional touch without any complex timing calculations.
Tailwind CSS Motion provides several built-in presets like motion-preset-fade, motion-preset-slide-up, motion-preset-bounce, and more. You can also compose custom animations using granular utilities like motion-translate-x-in-25, motion-opacity-in-0, motion-rotate-in-90, and motion-scale-in-50 for complete control over every animation dimension.
Tailwind CSS Motion pros
- Zero JavaScript overhead: Pure CSS animations mean no additional runtime cost.
- Tailwind-native syntax: Integrates seamlessly with your existing utility-first workflow.
- Bundle size: Extremely lightweight at approximately 5KB, adding minimal overhead to your CSS bundle.
- Visual animator: The Rombo Chrome Extension lets you create animations visually and export them as Tailwind classes.
- TypeScript support: Full type definitions out of the box for theme customizations and plugin configuration.
- Composable utilities: Combine granular utilities to create complex custom animations without writing keyframes.
- Popularity: 3.3k stars in GitHub, and 33K weekly downloads on npm, and growing adoption in Tailwind-based projects.
Tailwind CSS Motion cons
- Requires Tailwind CSS: Only works within Tailwind projects, not suitable for non-Tailwind codebases.
- Limited dynamic control: Since animations are CSS-based, complex interactive animations requiring JavaScript state may need additional solutions.
- Newer library: Smaller community compared to established libraries lik Motion or GSAP.
Putting our animation approaches to the test
To get a fair comparison, we implemented the same multi-layer parallax scrolling effect with each library. The animation requirements were:
Parallax requirements
- Multi-layer depth: 3-4 layers of mountain images moving at different speeds.
- Scroll synchronization: Smooth parallax effect tied to scroll position.
- Entrance animations: Title and subtitle with complex entrance effects (scale, rotate, fade).
- Scroll-triggered content: Cards and sections that animate when scrolled into view.
- Stagger effects: Multiple elements animating sequentially.
- Performance: Smooth 60fps scrolling with large background images.
Evaluation criteria
We are evaluating these libraries on these criteria:
- Bundle size: Production build size (gzipped)
- Implementation complexity: Lines of code and API ergonomics
- Scroll performance: Frame rate during parallax scrolling
- Developer experience: API clarity, TypeScript support, documentation
- Feature completeness: Scroll hooks, transforms, stagger, timeline control
These were our contenders:
- TailwindCSS Motion
- Motion(
- React Spring
- GSAP
- Anime.js
- Native CSS
Here’s why we selected these six:
Libraries we included:
- TailwindCSS Motion: A CSS-first approach that’s gaining rapid adoption in Tailwind-based projects, with much adoption, offering zero-JS animation options.
- Motion(Framer Motion): The most popular React animation library with excellent DX and comprehensive features.
- React Spring – Physics-based animations with a strong community and unique approach to motion.
- GSAP – The industry standard for professional-grade scroll and timeline animations.
- Anime.js – Lightweight JavaScript library with strong SVG support and versatile animation capabilities.
Libraries we excluded from the benchmark:
- React Transition Group: No longer actively maintained by its authors, making it less relevant for 2026 projects.
- React Move: Specialized for data visualization with D3.js integration; less suited for general UI animations like parallax scrolling.
- Remotion: Purpose-built for programmatic video creation, not real-time web interactions, making it incompatible with our scroll-based benchmark.
We also included native CSS as a baseline to demonstrate that modern CSS (with scroll-timeline, transforms, and transitions) can handle many animation needs without any library overhead.
Results of our animation library test
After implementing the parallax component and running a production build, here are the results:
| Library | Bundle Size (Gzipped) | Node Modules | Implementation LOC | Scroll Performance | DX Score | Best For |
|---|---|---|---|---|---|---|
| Native CSS | 0 KB | 0 KB | 322 lines | ![]() ![]() ![]() ![]() Excellent |
7/10 | Zero-dependency sites |
| Tailwindcss Motion | ~5 KB (CSS) | 492 KB | 128 lines | ![]() ![]() ![]() ![]() Excellent |
9/10 | Tailwind-based projects |
| React Spring | ~45 KB | 1.0 MB | 213 lines | ![]() ![]() ![]() Smooth |
7/10 | Physics-based motion |
| Anime.js | ~52 KB | 1.9 MB | 258 lines | ![]() ![]() ![]() Excellent |
7/10 | SVG/Timeline sequences |
| GSAP | ~78 KB | 6.2 MB | 309 lines | ![]() ![]() Good |
7/10 | Professional scrolling |
| Framer Motion | ~85 KB | 3.0 MB | 188 lines | ![]() ![]() ![]() Smooth |
10/10 | Complex interactions |
Total Combined Bundle: 560.49 KB (192.77 KB gzipped) – includes React Router and all libraries.
Note: Remotion was excluded from parallax testing as it’s designed for programmatic video creation, not real-time web interactions.
Key takeaways
Winner: Tailwindcss Motion (5KB, pure CSS) for simple animations; Motion (85KB, great DX) for complex scroll interactions; GSAP (78KB, pro-grade) when you need bulletproof performance.
When NOT to use these stacks (TailwindCSS Motion, Motion, and GSAP)
- React Native: Use React Native Reanimated or React Spring
- Video creation: Use Remotion for programmatic video generation
- Data visualizations: Use D3.js or Recharts for chart animations
- Game development: Use PixiJS or Three.js for canvas/WebGL animations
- Legacy browser support: Native CSS with careful polyfills
Migration checklist
If you’d like to migrate to any of these stacks, here are lists of things you must consider:
- Audit existing animations (identify simple vs complex)
- Install
tailwindcss-motionfor basic UI transitions - Replace simple animations with utility classes
- Install
framer-motionfor parallax/scroll effects - Lazy load Motion components (
React.lazy()) - Use
useScroll()+useTransform()for parallax layers - Remove deprecated libraries (React Reveal, React Transition Group)
- Run bundle analysis (
vite-bundle-visualizer) - Test scroll performance on low-end devices
- Consider GSAP only for complex timeline requirements
Performance tips
Here are some performance tips that will impact your use of these libraries for animations:
- Lazy load heavy libraries with
React.lazy()and Suspense. - Debounce scroll listeners (or use passive listeners).
- Optimize images: Large parallax images impact performance more than library choice.
- Use
transformovertop/left: Hardware-accelerated. - Limit parallax layers: 3-4 layers max for smooth 60fps.
Conclusion
From our comparison, Tailwindcss Motion will be our go-to for simple animations with decent developer experience, while Motion will be more patronised for complex scroll interactions. GSAP remains a professional choice for me when it comes to advanced requirements.
In terms of popularity, Anime.js leads GitHub stars (65.8k) and React Transition Group tops npm downloads (14M), though note that React Transition Group is no longer actively maintained.
Something to remember is that image optimization impacts performance more than library choice. Many of these libraries are customizable and include great built-in features and changes. Hopefully, going through the pros and cons, you can choose the right library for your next React application.
The post Comparing the best React animation libraries for 2026 appeared first on LogRocket Blog.
This post first appeared on Read More



