Framer Motion vs Motion One vs AutoAnimate: React Animations 2026
AutoAnimate's useAutoAnimate hook adds smooth list animations in one line of code. Motion One's animate() function is 3.8 kB. Framer Motion (now just "Motion") is 30+ kB with 3.6 million weekly downloads. These three libraries represent three completely different points on the animation complexity spectrum, and most React applications need only one of them.
TL;DR
AutoAnimate for 90% of cases where you just need smooth DOM transitions (adding/removing/reordering list items). Motion (Framer Motion) for everything else where you need real animation control — gestures, scroll-triggered animations, shared layouts, complex sequences. Motion One when you need Framer Motion-quality animations but your bundle budget won't allow 30 kB.
Key Takeaways
- Motion (Framer Motion): 3.6M weekly downloads, 30K+ GitHub stars, 30 kB gzipped
- AutoAnimate: 13K+ GitHub stars, 200K+ weekly downloads, <3 kB
- Motion One: Lighter alternative at 3.8 kB for
animate(), uses Web Animations API - Framer Motion renamed to "Motion" in 2024, now unified
motionpackage - Motion's
LazyMotion+domAnimationreduces bundle from 30 kB to 15 kB - AutoAnimate works with any framework (React, Vue, Angular, vanilla JS)
- Motion One is built on Web Animations API (WAAPI) — GPU-accelerated by browser
The Animation Library Spectrum
AutoAnimate Motion One Motion (Framer)
[Magical, simple] [Performant, small] [Powerful, feature-rich]
3 kB 3.8 kB 30 kB
One hook Full animation API Complete animation system
AutoAnimate
Package: @formkit/auto-animate
Weekly downloads: 200K+
GitHub stars: 13K
Bundle: <3 kB
AutoAnimate is the simplest animation library in existence. Drop it in, and your DOM transitions become smooth automatically:
import { useAutoAnimate } from '@formkit/auto-animate/react';
function TodoList() {
const [parent] = useAutoAnimate();
const [todos, setTodos] = useState(['Task 1', 'Task 2', 'Task 3']);
return (
<ul ref={parent}> {/* <-- That's it! */}
{todos.map(todo => (
<li key={todo}>{todo}</li>
))}
</ul>
);
}
// Now adding/removing/reordering items animates smoothly
// No animation code required — AutoAnimate handles it all
How It Works
AutoAnimate uses a MutationObserver to detect DOM changes, captures the before/after positions of elements, and animates the transition using the Web Animations API. Zero configuration.
Framework Agnostic
// Vue
import { vAutoAnimate } from '@formkit/auto-animate/vue';
<ul v-auto-animate>
// Vanilla JS
import autoAnimate from '@formkit/auto-animate';
autoAnimate(document.getElementById('list'));
// React
const [parent] = useAutoAnimate();
<div ref={parent}>
Custom Easing
You can customize the animation:
const [parent] = useAutoAnimate({
duration: 250,
easing: 'ease-in-out',
disrespectUserMotionPreference: false, // Respects prefers-reduced-motion
});
Limitations
- Only animates entering/leaving/reordering — no complex keyframe sequences
- Limited control over individual element animations
- Can't animate CSS properties directly
- No gesture support
Perfect for: Filtered lists, tab content, accordion panels, notification toasts, sortable drag-and-drop reorders.
Motion One
Package: motion (same package as Framer Motion, different API surface)
Weekly downloads: Part of motion package (3.6M)
Bundle: animate() function = 3.8 kB
Confusingly, Motion One is now part of the motion package (which is also Framer Motion). The motion/mini entry point gives you the Motion One API (Web Animations API-based):
npm install motion
// Motion One API (WAAPI-based, ultra-small)
import { animate, stagger } from 'motion';
// Animate a single element
animate('.button', { scale: [1, 1.1, 1] }, { duration: 0.3 });
// Animate multiple elements with stagger
animate('.list-item', { opacity: [0, 1], y: [-20, 0] }, {
duration: 0.4,
delay: stagger(0.1), // Each item starts 100ms after the previous
});
// Scroll-triggered animation
import { inView } from 'motion';
inView('.section', ({ target }) => {
animate(target, { opacity: [0, 1], y: [30, 0] }, { duration: 0.5 });
});
Why WAAPI Matters
Motion One uses the browser's native Web Animations API instead of JavaScript-driven animations. This means:
- Animations run on a separate thread (no main thread jank)
- GPU-accelerated automatically
- Works with
will-changehints for GPU compositing - Better mobile performance (especially on low-end devices)
React Integration
import { animate, AnimatePresence, useAnimate, motion } from 'motion/react';
// But 'motion/react' is Framer Motion — use the vanilla API for smallest bundle:
import { animate, stagger } from 'motion'; // WAAPI, 3.8 kB
function AnimatedList({ items }: { items: string[] }) {
const containerRef = useRef<HTMLUListElement>(null);
useEffect(() => {
if (containerRef.current) {
animate(
containerRef.current.children,
{ opacity: [0, 1], x: [-20, 0] },
{ delay: stagger(0.05) }
);
}
}, [items]);
return (
<ul ref={containerRef}>
{items.map(item => <li key={item}>{item}</li>)}
</ul>
);
}
Motion (Framer Motion)
Package: motion (renamed from framer-motion in 2024)
Weekly downloads: 3.6M
GitHub stars: 30K
Bundle: ~30 kB gzipped (full), ~15 kB with LazyMotion
Motion is the most comprehensive React animation library. It's the right tool when you need features that simpler libraries can't provide: layout animations, exit animations, gesture recognition, scroll-linked animations, and shared element transitions.
Core Usage
import { motion, AnimatePresence } from 'motion/react';
function Card({ isVisible }: { isVisible: boolean }) {
return (
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3, ease: 'easeOut' }}
className="card"
>
Card content
</motion.div>
)}
</AnimatePresence>
);
}
Layout Animations
Motion's killer feature: smooth layout animations that work across the DOM:
import { motion, LayoutGroup } from 'motion/react';
function FilteredList({ filter }: { filter: string }) {
const items = data.filter(item => item.category.includes(filter));
return (
<LayoutGroup>
<ul>
{items.map(item => (
<motion.li
key={item.id}
layout // <-- Animate when position changes due to filtering
layoutId={item.id} // <-- Enable shared element transitions
>
{item.name}
</motion.li>
))}
</ul>
</LayoutGroup>
);
}
Shared Element Transitions
// When navigating between pages, shared elements animate between positions
<motion.img
layoutId={`product-image-${product.id}`}
src={product.image}
/>
Gesture Animations
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
whileFocus={{ boxShadow: '0 0 0 3px rgba(0,0,0,0.2)' }}
>
Click me
</motion.button>
Scroll-Linked Animations
import { useScroll, useTransform, motion } from 'motion/react';
function ParallaxHero() {
const { scrollYProgress } = useScroll();
const y = useTransform(scrollYProgress, [0, 1], [0, -100]);
const opacity = useTransform(scrollYProgress, [0, 0.3], [1, 0]);
return (
<motion.div style={{ y, opacity }}>
Hero content
</motion.div>
);
}
Bundle Optimization
// Reduce bundle with LazyMotion
import { LazyMotion, domAnimation, m } from 'motion/react';
// Wrap your app (or section) with LazyMotion
function App() {
return (
<LazyMotion features={domAnimation}> {/* 15 kB instead of 30 kB */}
<m.div animate={{ opacity: 1 }} /> {/* Use 'm' instead of 'motion' */}
</LazyMotion>
);
}
Feature Comparison
| Feature | AutoAnimate | Motion One | Motion (Framer) |
|---|---|---|---|
| Bundle size | <3 kB | 3.8 kB | 30 kB (15 kB lazy) |
| Ease of use | Trivial | Easy | Moderate |
| DOM enter/exit | Auto | Manual | Manual |
| Layout animations | No | No | Yes |
| Shared elements | No | No | Yes |
| Gesture detection | No | No | Yes |
| Scroll-linked | No | Yes | Yes |
| Exit animations | No | Manual | AnimatePresence |
| WAAPI-based | Yes | Yes | Hybrid |
| React-specific | Yes | No | Yes |
| TypeScript | Good | Good | Excellent |
Performance
| Library | CPU Usage | GPU Acceleration | Mobile |
|---|---|---|---|
| AutoAnimate | Low | Partial | Good |
| Motion One | Very Low | Full (WAAPI) | Excellent |
| Motion (Framer) | Medium | Partial (transforms) | Good |
Motion One's WAAPI foundation gives it the best mobile performance. Framer Motion's JavaScript orchestration enables more complex sequencing at the cost of some CPU usage.
Decision Framework
Use AutoAnimate if:
- You want animations for free (lists, accordions, tab panels)
- You're adding animations to existing components with no refactor
- Your animation needs are 90% enter/exit/reorder
- Bundle size is a concern
Use Motion One if:
- You need choreographed multi-element animations
- Scroll-triggered reveal animations are needed
- Mobile performance is critical
- Bundle budget is tight but AutoAnimate isn't enough
Use Motion (Framer Motion) if:
- Layout animations are required (reordering, resizing, filtering)
- Shared element transitions between pages/views
- Gesture-driven interactions (drag, pinch, etc.)
- Complex animation sequences with callbacks
- You're in a Next.js app and page transition animations matter
Practical Patterns
The 3-Tool Stack (2026 Best Practice)
Many production React apps use all three:
// AutoAnimate for list transitions
const [listRef] = useAutoAnimate();
// Motion for interactive UI
<motion.button whileHover={{ scale: 1.05 }} />
// Motion One for scroll reveals (smaller than importing full Motion)
import { inView } from 'motion';
inView('.hero', ({ target }) => {
animate(target, { opacity: [0, 1] });
});
Compare on PkgPulse
Track download trends for Framer Motion vs animation libraries on PkgPulse.
See the live comparison
View framer motion vs. motion one on PkgPulse →