This comparison evaluates seven prominent animation libraries used in modern web development, specifically focusing on their integration with React and vanilla JavaScript environments. framer-motion and react-spring represent the modern React-native approach, leveraging hooks and declarative syntax. gsap and animejs offer robust imperative control suitable for complex timelines and vanilla JS contexts. react-transition-group provides low-level CSS transition management, often serving as a foundation for other UI libraries. react-motion is a legacy physics-based library that is no longer maintained, while animated typically refers to polyfills or declarative wrappers often tied to React Native ecosystems. Understanding these distinctions helps teams select the right tool for performance, maintainability, and design fidelity.
Animation in modern web development is not just about making things move — it is about guiding user attention, providing feedback, and creating a sense of continuity. The JavaScript ecosystem offers a wide range of tools for this purpose, from low-level CSS managers to high-level physics engines. This analysis compares seven key packages: animated, animejs, framer-motion, gsap, react-motion, react-spring, and react-transition-group. We will examine how they handle state, performance, and integration patterns to help you make informed architectural decisions.
The fundamental split in these libraries is between declarative React-centric tools and imperative vanilla JavaScript engines.
framer-motion and react-spring are declarative. You describe the end state, and the library handles the interpolation. This aligns with React's mental model.
// framer-motion: Declarative state
import { motion } from 'framer-motion';
function Box() {
return <motion.div animate={{ x: 100, opacity: 1 }} />;
}
// react-spring: Hook-based interpolation
import { useSpring, animated } from '@react-spring/web';
function Box() {
const props = useSpring({ to: { x: 100, opacity: 1 }, from: { x: 0, opacity: 0 } });
return <animated.div style={props} />;
}
gsap and animejs are imperative. You create timelines or tweens and control them via methods. This gives precise control but requires managing refs outside React's state.
// gsap: Imperative timeline
import { gsap } from 'gsap';
import { useRef, useEffect } from 'react';
function Box() {
const ref = useRef(null);
useEffect(() => {
gsap.to(ref.current, { x: 100, opacity: 1, duration: 1 });
}, []);
return <div ref={ref} />;
}
// animejs: Imperative targets
import anime from 'animejs';
import { useRef, useEffect } from 'react';
function Box() {
const ref = useRef(null);
useEffect(() => {
anime({ targets: ref.current, translateX: 100, opacity: 1 });
}, []);
return <div ref={ref} />;
}
react-transition-group is a lifecycle manager. It does not animate values directly but toggles classes.
// react-transition-group: Class toggling
import { CSSTransition } from 'react-transition-group';
function Box({ show }) {
return (
<CSSTransition in={show} timeout={300} classNames="fade">
<div /> {/* CSS handles the .fade-enter-active logic */}
</CSSTransition>
);
}
animated is often a polyfill or wrapper for React Native's API on the web, offering a declarative value approach similar to react-spring but with less web-specific optimization.
// animated: Value interpolation
import { Animated } from 'animated'; // Web polyfill variant
import { useRef, useEffect } from 'react';
function Box() {
const val = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(val, { toValue: 1, duration: 300 }).start();
}, []);
return <Animated.View style={{ opacity: val }} />;
}
One critical factor in architectural decisions is the long-term viability of the library.
react-motion is no longer maintained. The repository has not seen significant updates in years, and it does not support modern React features like concurrent mode well. It introduced spring physics to React but has been superseded by react-spring.
// react-motion: Legacy (Do not use in new projects)
import { Motion, spring } from 'react-motion';
function Box() {
return (
<Motion defaultStyle={{ x: 0 }} style={{ x: spring(100) }}>
{props => <div style={{ transform: `translateX(${props.x}px)` }} />}
</Motion>
);
}
If you encounter react-motion in an existing codebase, plan to migrate to react-spring or framer-motion to ensure compatibility with future React updates.
Complex animations often require sequencing — running one animation after another or coordinating multiple elements.
gsap excels here with its Timeline API. You can stack animations precisely without calculating delays manually.
// gsap: Timeline sequencing
const tl = gsap.timeline();
tl.to(".box1", { x: 100 })
.to(".box2", { x: 100 }, "-=0.5"); // Overlap by 0.5s
animejs also supports timelines, allowing staggered effects easily.
// animejs: Staggered timeline
anime.timeline({ loop: false })
.add({ targets: '.box1', translateX: 100 })
.add({ targets: '.box2', translateX: 100 }, '-=500');
framer-motion handles sequencing via the transition prop and staggerChildren in variants, which is more React-friendly but less granular than GSAP.
// framer-motion: Variants staggering
const variants = {
visible: { opacity: 1, transition: { staggerChildren: 0.1 } }
};
// Apply to parent, children animate in sequence automatically
react-spring uses useChain or delay calculations within hooks for sequencing.
// react-spring: useChain
import { useChain, useSpring, useSpringRef } from '@react-spring/web';
const ref1 = useSpringRef();
const spring1 = useSpring({ ref: ref1, from: { x: 0 }, to: { x: 100 } });
// ref1.start() manages the timing
react-transition-group and animated do not have built-in timeline engines. You must manage sequencing manually via state or callbacks.
How a library interacts with the browser's paint cycle affects performance.
framer-motion and react-spring optimize for React. They use requestAnimationFrame and avoid triggering React re-renders for every frame by using refs for mutable values where possible.
gsap and animejs bypass React entirely for the animation loop. They modify DOM properties directly. This is often faster for heavy animations but can conflict with React's DOM management if refs are not handled carefully.
react-transition-group relies on CSS transitions. The browser handles the interpolation, which is highly performant (GPU accelerated) but limited to what CSS can do.
react-motion suffered from performance issues because it triggered React re-renders on every frame in older versions, which is costly.
Each library requires a different integration pattern within your components.
| Package | Pattern | React Integration | Best For |
|---|---|---|---|
framer-motion | Component-based | High (Native) | UI interactions, layouts |
react-spring | Hook-based | High (Native) | Physics, dynamic values |
gsap | Imperative Refs | Medium (Via useEffect) | Complex timelines, scroll |
animejs | Imperative Selectors | Medium (Via useEffect) | SVG, lightweight effects |
react-transition-group | Lifecycle Wrapper | High (Native) | CSS class swaps, lists |
animated | Value-based | Medium (Polyfill) | React Native web porting |
react-motion | Render Props | Low (Legacy) | Legacy maintenance only |
For most modern React applications, framer-motion offers the best balance of power and ease of use. It handles layout animations and gestures better than any other React-specific library. If you need physics-based interactions that feel organic, react-spring is the superior choice.
For marketing sites or experiences requiring complex, synchronized timelines that go beyond React's component tree, gsap remains the industry standard. animejs is a valid alternative if you need a lighter footprint without GSAP's licensing considerations.
Avoid react-motion in new development. Use react-transition-group only if you are building a UI library yourself or need specific CSS transition lifecycle control. Choose animated only if you are sharing code between React Native and Web and need API parity.
Selecting the right tool depends on whether you prioritize developer experience within React or raw animation power outside of it. Both paths are valid — just ensure your choice aligns with your team's skills and project requirements.
Choose framer-motion if you are building a React application that requires declarative animations, layout transitions, and gesture handling out of the box. It offers the best developer experience for React developers due to its component-based API and automatic optimization. It is the go-to for interactive UIs, page transitions, and micro-interactions within the React ecosystem.
Choose animated if you are working primarily in a React Native environment and need a web polyfill, or if you require a simple declarative wrapper for basic value interpolation. It is less suitable for complex web-specific animations compared to dedicated web libraries. Use this only if your stack heavily relies on React Native's Animated API semantics across platforms.
Choose animejs if you need a lightweight, vanilla JavaScript solution that works independently of React's render cycle. It is ideal for animating SVG paths, complex timelines, or DOM elements directly without managing React state. This is a strong choice for marketing sites or visual experiences where performance and fine-grained control matter more than React integration.
Choose gsap if you need industry-standard timeline control, complex sequencing, or high-performance animations that span across multiple DOM elements. It is perfect for award-winning websites, intricate scroll-triggered effects, and scenarios where imperative control is preferred over declarative state. Note that some advanced plugins require a commercial license.
Do NOT choose react-motion for new projects as it is no longer maintained and has known performance issues with modern React versions. It was a pioneer in spring physics but has been superseded by react-spring. Only consider this if you are maintaining a legacy codebase that cannot be refactored immediately.
Choose react-spring if you want physics-based animations that feel natural and responsive within a React application. It provides a powerful hook-based API that integrates deeply with React's render cycle without causing unnecessary re-renders. It is ideal for interactive elements like draggables, toggles, and dynamic layouts where spring dynamics are preferred over fixed durations.
Choose react-transition-group if you need low-level control over CSS class switching during enter/exit phases in React. It is best used when building your own animation components or when working with UI libraries that rely on it internally. It is not a full animation engine but rather a utility for managing component lifecycle states tied to CSS transitions.
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.