Skip to main content

Framer Motion vs Motion One vs AutoAnimate 2026

·PkgPulse Team
0

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.

Ecosystem and Community

Motion (Framer Motion) has the largest animation library community in the React ecosystem. The GitHub repository has 30,000+ stars and an extremely active Discussions section where maintainer Matt Perry is frequently seen answering questions. The Framer design tool's use of Motion for production UI means there's a constant stream of high-quality examples from Framer's own team. Community resources include hundreds of CodeSandbox examples, YouTube tutorials, and dedicated courses. For pre-built animated components that use Framer Motion under the hood, see Aceternity UI vs Magic UI vs shadcn animated React components.

The motion npm package consolidation — merging the old framer-motion and motion packages into one — simplified the ecosystem. All existing framer-motion imports work via an automatic re-export, so the migration was transparent for users. The Motion website at motion.dev has become a definitive reference for WAAPI patterns in JavaScript.

AutoAnimate's community is small but focused. The FormKit team (creators of Vue's best form library) created and maintains it. While it's framework-agnostic, the React hook is the primary use case. The GitHub issues are resolved quickly, and the library's simplicity means there are relatively few edge cases to work through. The most common community question is about animating height transitions, which requires a small workaround since AutoAnimate doesn't directly support height: 0 to height: auto transitions.

Real-World Adoption

Motion (Framer Motion) is present in a significant percentage of polished React applications. The motion.div pattern with initial, animate, and exit props has become idiomatic React UI code. Vercel's own marketing site, numerous design agencies, and hundreds of product landing pages use it for page transition animations and micro-interactions. The layout animation feature specifically is what separates it from CSS transitions — when a filter removes items from a list and the remaining items smoothly animate into their new positions, that's Motion's layout prop doing work no CSS transition can match.

AutoAnimate sees heavy adoption in utility-focused applications where the developer doesn't want to think about animations but wants the UI to feel polished. Admin dashboards, project management tools, and developer tools frequently use it for list operations. The pattern of "add AutoAnimate to every list and accordion" has become a common final polish step before shipping. For testing that your animations don't regress, see best JavaScript testing frameworks 2026.

Motion One's adoption is concentrated in performance-critical applications and teams with a strong opinion on bundle size. It's particularly popular in the games-adjacent web space, interactive data visualization, and progressive web apps where runtime performance on mid-range mobile devices matters significantly.

Developer Experience Deep Dive

Motion's TypeScript support is excellent. The motion.div and other component variants are strongly typed, animation variant types are inferred correctly, and the hook APIs (useScroll, useTransform, useAnimate) all return typed values. The main DX friction point is understanding the relationship between initial, animate, exit, and variants — the declarative model can be counterintuitive when you're used to imperative animation code.

The useAnimate hook, added in Motion v10, provides an imperative API that's easier to use for sequential animations and precise timing control:

import { useAnimate } from 'motion/react';

function SuccessAnimation() {
  const [scope, animate] = useAnimate();

  async function handleSuccess() {
    // Sequence animations imperatively
    await animate(scope.current, { scale: 1.2 }, { duration: 0.2 });
    await animate(scope.current, { scale: 1 }, { duration: 0.1 });
    animate('.success-text', { opacity: 1, y: 0 }, { duration: 0.3 });
  }

  return (
    <div ref={scope}>
      <button onClick={handleSuccess}>Submit</button>
      <p className="success-text" style={{ opacity: 0, transform: 'translateY(10px)' }}>
        Success!
      </p>
    </div>
  );
}

AutoAnimate's developer experience is genuinely frictionless. A developer new to animations can add it to a component in two minutes and have a production-quality list animation. The main limitation is the lack of customization per-element — every element in an AutoAnimated container uses the same animation timing and easing. For 90% of use cases, this is fine.

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] });
});

This combination keeps the bundle small (AutoAnimate for free list animations, Motion One for scroll effects) while still having the full Motion library available for the complex interactive parts that need layout animations and gesture detection.

Final Verdict 2026

The default recommendation for a new React project: add @formkit/auto-animate on day one for all your list and conditional rendering transitions. Add motion (Framer Motion) when you need layout animations, shared element transitions, or gesture-driven interactions. Use Motion One's animate() and inView() for scroll-triggered reveal animations where you don't need the full Motion library's React integration.

The cost of adding all three to a project is modest — AutoAnimate is 3 kB, and the overlap between Motion One and Framer Motion's package means you're not paying double for the WAAPI functions. The benefit is a polished, animation-rich UI that would take weeks of custom code to replicate from scratch.

Track download trends for Framer Motion vs animation libraries on PkgPulse.

Accessibility Considerations

Animation and accessibility intersect in important ways. The prefers-reduced-motion media query is the standard mechanism for users to indicate they prefer less motion, and all three libraries handle it differently.

AutoAnimate respects prefers-reduced-motion by default. The disrespectUserMotionPreference: false default means that users who have enabled reduced motion in their OS settings will see instant transitions rather than animated ones. This is the correct behavior for a library focused on DOM transitions — it requires zero additional code from developers.

Motion (Framer Motion) does not automatically respect prefers-reduced-motion. You need to use the useReducedMotion hook and conditionally apply animations. This is more work but gives you precise control — you might want to keep subtle opacity transitions while disabling scale and position animations for users who prefer reduced motion. The motion.div component doesn't change its behavior based on the system preference unless you explicitly implement the check.

Motion One's animate() function similarly doesn't automatically respond to prefers-reduced-motion. You can check the media query with window.matchMedia('(prefers-reduced-motion: reduce)') and conditionally call animate(). For scroll-triggered animations using inView(), a common pattern is to check the preference before registering any callbacks.

For production React applications, a recommended pattern is to create a wrapper around Motion's components that automatically disables animations based on the media query:

import { motion, useReducedMotion } from 'motion/react';

// Accessible motion component that respects prefers-reduced-motion
export function AccessibleMotionDiv({ animate, ...props }) {
  const prefersReducedMotion = useReducedMotion();
  
  return (
    <motion.div
      {...props}
      animate={prefersReducedMotion ? {} : animate}
    />
  );
}

AutoAnimate's built-in behavior makes it the safest default for teams that want to add animations without worrying about accessibility regressions. For projects where Motion is already in use, adopting the useReducedMotion hook throughout the codebase is the recommended practice.

Bundle Impact in Real Applications

The 30 kB headline number for Motion (Framer Motion) deserves context. In a Next.js application with code splitting, Motion's bundle is only loaded for pages and components that use it. A landing page that uses Motion for scroll animations doesn't add Motion's bundle to the weight of the admin dashboard if that dashboard doesn't use Motion. Server Components in Next.js App Router never include Motion in their bundle since animations are client-side only.

The practical bundle impact for a typical Next.js app that uses Motion on the homepage and a few interactive components is 15-30 kB on those specific page routes — roughly equivalent to a medium-complexity chart library or a date-formatting library. For applications that are already 500 kB+ in total bundle, Motion's contribution is not a primary concern.

AutoAnimate's 3 kB is genuinely negligible. Adding it to an existing project has no measurable impact on Core Web Vitals. The same is true for Motion One's animate() function at 3.8 kB — it's smaller than a typical icon component set.

Related: Best React Component Libraries 2026, Best JavaScript Testing Frameworks 2026, Framer Motion vs Motion One compare. For pre-built animated components that work alongside these motion libraries, see Aceternity UI vs Magic UI vs shadcn animated React components.

Compare Framer Motion and Motion-one package health on PkgPulse.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.