This comparison evaluates six popular React animation libraries, ranging from modern physics-based engines to legacy CSS transition wrappers. framer-motion and react-spring represent the current standard for declarative, high-performance animations. react-transition-group remains the low-level primitive for CSS-based class transitions. react-motion, react-move, and react-animations are older solutions that are largely considered legacy or unmaintained in modern ecosystems. Understanding their architectural differences helps teams avoid technical debt while selecting the right tool for interaction design.
Choosing an animation library in React is not just about picking a visual effect — it is about selecting an engine that fits your application's architecture. Some libraries use physics to calculate values, while others rely on CSS classes or keyframes. This guide breaks down how six popular packages handle motion, interaction, and lifecycle events.
The underlying engine determines how smooth your animations feel and how much control you have.
framer-motion uses a declarative model backed by a powerful animation engine that supports both CSS transitions and spring physics.
// framer-motion: Declarative animate prop
import { motion } from "framer-motion";
<motion.div
animate={{ x: 100, opacity: 1 }}
transition={{ type: "spring" }}
/>
react-spring is built entirely on physics. Every animation is a spring that interpolates values over time.
// react-spring: Hook-based physics
import { useSpring, animated } from "@react-spring/web";
const props = useSpring({ to: { x: 100, opacity: 1 }, from: { x: 0, opacity: 0 } });
<animated.div style={props} />
react-transition-group does not calculate values. It toggles CSS classes at specific lifecycle moments.
// react-transition-group: CSS class toggling
import { CSSTransition } from "react-transition-group";
<CSSTransition in={show} timeout={300} classNames="fade">
<div /> {/* CSS handles .fade-enter-active */}
</CSSTransition>
react-motion uses a render prop to inject interpolated values based on spring physics.
// react-motion: Render prop injection
import { Motion, spring } from "react-motion";
<Motion style={{ x: spring(100) }}>
{({ x }) => <div style={{ transform: `translateX(${x}px)` }} />}
</Motion>
react-move also uses a render prop pattern similar to react-motion but with a different configuration API.
// react-move: Render prop with state
import { Move } from "react-move";
<Move data={{ x: 100 }}>
{({ data }) => <div style={{ transform: `translateX(${data.x}px)` }} />}
</Move>
react-animations provides predefined keyframe objects to be used with CSS-in-JS libraries.
// react-animations: Keyframe injection
import { fadeIn } from "react-animations";
import styled from "styled-components";
const Div = styled.div` animation: 1s ${fadeIn}`;
<div /> // Uses CSS @keyframes generated by library
Animating elements as they appear or disappear requires tracking state changes outside the normal render cycle.
framer-motion uses the AnimatePresence component to detect removal from the tree.
// framer-motion: AnimatePresence wrapper
import { AnimatePresence, motion } from "framer-motion";
<AnimatePresence>
{show && <motion.div exit={{ opacity: 0 }} />}
</AnimatePresence>
react-spring uses the useTransition hook to manage item lifecycle and interpolation.
// react-spring: useTransition hook
import { useTransition } from "@react-spring/web";
const transitions = useTransition(show, { from: { opacity: 0 }, to: { opacity: 1 } });
react-transition-group is specifically designed for this. The in prop triggers the CSS classes.
// react-transition-group: in prop control
import { Transition } from "react-transition-group";
<Transition in={show} timeout={300}>
{(state) => <div className={`item ${state}`} />}
</Transition>
react-motion requires manual handling or external state logic as it does not have a built-in presence system.
// react-motion: Manual visibility check
import { Motion, spring } from "react-motion";
{show && <Motion style={{ opacity: spring(1) }}>{({ opacity }) => <div />}</Motion>}
react-move similar to react-motion, relies on the parent to manage the existence of the component.
// react-move: Conditional rendering
import { Move } from "react-move";
{show && <Move data={{ opacity: 1 }}>{({ data }) => <div />}</Move>}
react-animations does not handle mount/unmount logic. It only styles the element once mounted.
// react-animations: No lifecycle support
import { fadeIn } from "react-animations";
// Must be combined with react-transition-group or similar for exit logic
<div style={{ animation: `${fadeIn} 1s` }} />
Modern apps require motion to respond to user input instantly without complex event listeners.
framer-motion has built-in props for common interactions like whileHover and drag.
// framer-motion: Built-in interaction props
<motion.button whileHover={{ scale: 1.1 }} drag />
react-spring requires using hooks like useSprings or combining with useGesture for interactions.
// react-spring: Manual event handlers
const [props, api] = useSpring(() => ({ scale: 1 }));
<div onMouseEnter={() => api({ scale: 1.1 })} style={props} />
react-transition-group does not support interaction states. It is strictly for lifecycle transitions.
// react-transition-group: No interaction support
// Must use standard CSS :hover or React state
<div className="button" /> // CSS handles :hover
react-motion requires manual event listeners to update spring values.
// react-motion: Manual state update
const [hover, setHover] = useState(0);
<Motion style={{ scale: spring(hover) }}>{({ scale }) => <div />}</Motion>
react-move similar to react-motion, needs manual event wiring to change data values.
// react-move: Manual data update
const [data, setData] = useState({ scale: 1 });
<Move data={data}>{({ data }) => <div />}</Move>
react-animations relies entirely on CSS pseudo-classes for interaction.
// react-animations: CSS only
const Button = styled.button` &:hover { animation: ${pulse} 1s}`;
<Button /> // No JS interaction logic
Animating layout shifts (like expanding a card or reordering a list) is notoriously difficult in CSS.
framer-motion features layout prop that automatically animates size and position changes.
// framer-motion: Automatic layout animation
<motion.div layout onClick={() => setIsExpanded(!isExpanded)} />
react-spring requires manual measurement of dimensions to interpolate values correctly.
// react-spring: Manual measurement
const { height } = useSpring({ height: isOpen ? 200 : 0 });
<animated.div style={{ height }} />
react-transition-group cannot animate layout changes dynamically without complex CSS transitions.
// react-transition-group: CSS max-height trick
// Requires knowing final height in advance for CSS
<div className={"transition-max-height " + (isOpen ? "open" : "")} />
react-motion can animate values but does not auto-detect layout changes.
// react-motion: Manual value interpolation
<Motion style={{ h: spring(isOpen ? 200 : 0) }}>{({ h }) => <div style={{ height: h }} />}</Motion>
react-move similar to react-motion, requires explicit target values for layout properties.
// react-move: Explicit target data
<Move data={{ height: isOpen ? 200 : 0 }}>{({ data }) => <div style={{ height: data.height }} />}</Move>
react-animations does not support dynamic layout animation. It is for fixed keyframes only.
// react-animations: No dynamic layout support
// Cannot animate to unknown heights with keyframes
<div /> // Static styles only
Using a library that is no longer maintained introduces security risks and compatibility issues with future React versions.
| Package | Status | React Version Support | Recommendation |
|---|---|---|---|
framer-motion | ✅ Active | 16.8+ (Hooks) | Primary Choice |
react-spring | ✅ Active | 16.8+ (Hooks) | Primary Choice |
react-transition-group | ✅ Active | 16+ | For CSS Transitions |
react-motion | ⚠️ Archived | 15/16 (Class/Render Props) | Avoid |
react-move | ⚠️ Unmaintained | 16+ | Avoid |
react-animations | ⚠️ Legacy | 15+ | Avoid |
For new projects, framer-motion is the safest bet. It handles complex layout shifts and gestures with minimal code. If you need pure physics control, react-spring is the expert choice. Use react-transition-group only for simple CSS class swaps. Avoid react-motion, react-move, and react-animations as they rely on older patterns and lack active support.
Final Thought: Animation libraries are infrastructure. Choosing a maintained one ensures your UI remains smooth as React evolves.
Choose framer-motion for most modern applications requiring complex interactions, layout animations, and gesture support. It offers the best balance of performance and developer experience with a declarative API. It is ideal for dashboards, interactive prototypes, and production apps needing polished motion without deep physics knowledge.
Avoid react-animations for new projects as it is a legacy library focused on predefined keyframes rather than dynamic state. It does not integrate well with modern React hooks or server-side rendering patterns. Use CSS modules or modern animation libraries instead for keyframe-based effects.
Avoid react-motion for new projects as it is effectively unmaintained and relies on older React patterns like render props. It was pioneering for physics animations but has been superseded by more performant and ergonomic libraries. Only consider it for maintaining legacy codebases that already depend on it.
Avoid react-move for new projects as it is a predecessor to react-spring and is no longer actively developed. It shares similar physics concepts but lacks the modern API improvements and community support of its successor. Migrating to react-spring is recommended for any project currently using this.
Choose react-spring if your design relies heavily on natural, physics-based movement like springs and damping. It provides granular control over interpolation and is excellent for data visualizations or games. It is suitable when you need a physics engine that integrates tightly with React hooks.
Choose react-transition-group for simple mount/unmount transitions controlled by CSS classes. It is the lightest option and works well when you only need to fade or slide elements in and out without complex value interpolation. It is best for basic lists, modals, or route transitions where CSS handles the heavy lifting.
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.