auto-animate

A zero-config, drop-in animation utility that adds smooth transitions to any web app with a single line of code.

Library
npm
v0.10.0
13,937 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
48 /100 Fair
Development Activity 16
Maintenance 20
Community 60
Maturity 56
Momentum 40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
80 /100 Excellent
Architecture 80
Code Quality 78
Innovation 82
Learning Curve 80

AutoAnimate is a small utility from the FormKit team that watches a parent element and automatically animates its children whenever they are added, removed, or reordered in the DOM. Instead of hand-writing enter/exit transitions or wiring up a full animation library, you call a single function on a container element and every child mutation — list items appearing, form fields toggling, cards being sorted — gets a smooth FLIP-style transition for free.

Under the hood it uses a MutationObserver to detect DOM changes and the native Web Animations API to run the transitions, so there are no CSS classes to manage and no animation state to track manually. It ships framework bindings for React, Vue, Solid, Preact, Angular, Svelte, Qwik, and Marko, plus a Nuxt module, so the same core engine is reachable through whichever component model a project already uses.

Because it operates directly on real DOM mutations rather than a virtual-DOM diff, AutoAnimate works even when elements are added or removed by code it doesn’t control — third-party widgets, non-React DOM manipulation, or plain vanilla JS — making it one of the few animation utilities that doesn’t require rewriting how a UI already manages its elements.

What You Get

  • A single autoAnimate(el, options) call that instruments a parent element and automatically animates add/remove/move mutations of its children
  • First-class bindings for React (useAutoAnimate), Vue (v-auto-animate directive and useAutoAnimate composable), Solid, Preact, Angular, Svelte, and Qwik
  • A Marko custom tag and a Nuxt module for drop-in use in those ecosystems without extra glue code
  • An AnimationController returned from every binding, exposing enable(), disable(), and destroy() for runtime control over a given container
  • A plugin system (AutoAnimationPlugin) that lets you supply custom KeyframeEffect keyframes for add/remove/remain instead of the default scale/opacity transition
  • Automatic respect for the user’s prefers-reduced-motion setting, with an explicit opt-out flag for cases where that’s not desired

Common Use Cases

  • Animating list additions and removals (to-do lists, notification feeds, search results) without writing enter/exit CSS
  • Smoothly reordering sortable lists or Kanban-style boards as items move between positions
  • Adding polish to dropdowns, accordions, and toggled form sections that mount/unmount children
  • Retrofitting motion onto an existing app’s DOM updates without restructuring how the app already renders lists

Under The Hood

Architecture

The core lives in a single src/index.ts module that keeps all instrumented-element state in WeakMaps and WeakSets keyed by DOM node (coordinates, sibling references, in-flight animations, intersection/mutation observers, per-element options) plus one non-weak Set tracking instrumented parent containers. Framework bindings (React, Vue, Solid, Preact, Angular, Qwik, Marko, plus a Nuxt module) are thin adapters that call this same core autoAnimate() function and wrap its returned AnimationController lifecycle in each framework’s own idioms — useState/useEffect in React, watchEffect/onBeforeUnmount in Vue, createSignal/onCleanup in Solid. Data flows from a MutationObserver detecting childList changes, through handleMutations/getElements resolving which elements were actually affected (including short-circuiting on comment-only mutations), into animate() branching into add/remain/remove based on existing WeakMap state, which drives FLIP-style transforms through the Web Animations API. Because every binding imports directly from the core module with no isolating adapter layer, a breaking change to the core’s public shape (autoAnimate, AnimationController) would immediately break all eight framework integrations at once.

Tech Stack

Written in strict-mode TypeScript targeting ES2019, compiled via a custom Rollup/unbuild pipeline driven through jiti (build/build.mjs, build/bundle.ts). The published package has zero runtime dependencies — every framework listed in devDependencies (React 19, Vue 3.5, Solid, Angular, Marko, Nuxt tooling) exists only to compile and test that framework’s binding, never to run the library itself. End-to-end and visual-regression testing runs on Playwright against a Vite-served docs/demo app, with pixelmatch/pngjs doing pixel-diffing for visual assertions. The project builds pure ESM output (type: module, .mjs files) with per-framework subpath exports (./vue, ./react, ./solid, etc.), each carrying independent type declarations, and ships releases through bumpp via GitHub Actions.

Code Quality

Testing is entirely Playwright-based end-to-end and visual specs (list/dropdown animation triggers, Vue v-if behavior, offscreen-element handling, a scroll-jump regression test, memory-leak checks gated behind a LEAK_STRICT env flag, and dedicated Marko SSR specs) rather than isolated unit tests of internal helpers like coordinate math or transition-size calculation. TypeScript strict mode is enabled project-wide and the core module is densely JSDoc-commented per internal function. Error handling is intentionally minimal and defensive — expected AbortErrors from cancelled Web Animations are swallowed in empty catch blocks rather than surfaced through a typed error path, which is a reasonable tradeoff for a DOM-observer library but does mean failure modes are largely implicit. No dedicated lint/format config is checked into the repo root, leaving TypeScript’s own strictness as the main consistency guardrail.

API Design The entire public surface for the default case is one function call — autoAnimate(el) — with framework bindings reducing that further to a single hook or directive (useAutoAnimate(), v-auto-animate), which is unusually low ceremony compared to transition libraries that require explicitly authoring enter/exit states per element (React’s TransitionGroup, Vue’s <transition-group>). Its core novelty is deriving transitions purely from observed MutationObserver events rather than framework lifecycle hooks or virtual-DOM diffing, so it animates DOM changes made by any code touching that subtree — not just the framework binding installed — while still exposing a plugin escape hatch (a function returning a raw KeyframeEffect) for teams that need to fully customize the animation beyond the built-in scale/opacity/transform defaults.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers