Skip to main content

Framer Motion vs Motion One vs AutoAnimate: React Animations 2026

·PkgPulse Team

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 motion package
  • Motion's LazyMotion + domAnimation reduces 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-change hints 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

FeatureAutoAnimateMotion OneMotion (Framer)
Bundle size<3 kB3.8 kB30 kB (15 kB lazy)
Ease of useTrivialEasyModerate
DOM enter/exitAutoManualManual
Layout animationsNoNoYes
Shared elementsNoNoYes
Gesture detectionNoNoYes
Scroll-linkedNoYesYes
Exit animationsNoManualAnimatePresence
WAAPI-basedYesYesHybrid
React-specificYesNoYes
TypeScriptGoodGoodExcellent

Performance

LibraryCPU UsageGPU AccelerationMobile
AutoAnimateLowPartialGood
Motion OneVery LowFull (WAAPI)Excellent
Motion (Framer)MediumPartial (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.

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.