@vueuse/motion

Vue composables and a v-motion directive for declarative, physics-based component animation.

Library
npm
v3.0.3
2,759stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
46/100Fair
Development Activity0
Maintenance32
Community52
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
78/100Good
Architecture80
Code Quality78
Innovation70
Learning Curve85

@vueuse/motion brings Framer Motion-style declarative animation to the Vue Composition API. Instead of hand-writing CSS transitions or wiring up a general-purpose animation engine, developers attach a v-motion directive (or call useMotion directly) with initial, enter, visible, hover, and other named variants, and the library drives the underlying style and transform properties through a spring- or tween-based engine built on Popmotion.

The package ships 20+ ready-made presets (fade, pop, slide, roll — each with visible/visible-once variants driven by IntersectionObserver), a <Motion> / <MotionGroup> component pair for JSX-style usage, and first-class SSR support including a getSSRProps hook so initial animation state renders correctly on the server. A dedicated Nuxt 3 module auto-registers the plugin and composables. Everything is written in TypeScript with a small, tree-shakeable footprint (under 25kb) and no dependency on a virtual DOM diffing scheme beyond Vue’s own reactivity.

What You Get

  • v-motion directive with per-element initial, enter, visible, visible-once, hovered, tapped, and custom variant states
  • 20+ built-in presets (fade, pop, slide, roll, each with directional and scroll-triggered variants) registered as v-motion-* directives
  • <Motion> and <MotionGroup> components for JSX/render-function usage as an alternative to the directive
  • Low-level composables (useMotion, useMotionProperties, useMotionControls, useSpring, reactiveStyle, reactiveTransform) for building fully custom animation logic
  • SSR-safe initial state rendering via directive getSSRProps, so server-rendered markup matches the pre-animation state
  • A dedicated Nuxt 3 module that auto-registers the plugin, directives, and composables with zero manual setup
  • useReducedMotion composable to respect the user’s OS-level reduced-motion preference

Common Use Cases

  • Animating route/page transitions and mounted-component entrances in a Vue or Nuxt app
  • Scroll-triggered reveal animations (fade/slide/roll-in) for marketing and content pages using the visible/visible-once variants
  • Building custom hover/tap micro-interactions on buttons, cards, and list items without hand-rolled CSS transitions
  • Orchestrating staggered list/group animations with <MotionGroup> when items are added or removed
  • Driving physics-based (spring) animations for drag-and-drop or gesture-following UI via useSpring and useMotionValues directly

Under The Hood

Architecture The library is organized around a layered composable architecture: useMotion (src/useMotion.ts) composes four smaller composables — useMotionProperties (reactive style/transform state), useMotionVariants (variant/state resolution), useMotionControls (imperative apply/stop/set/leave API), and useMotionFeatures (binds event listeners, lifecycle hooks, variant syncing, and visibility hooks based on options) — each living in its own file under src/. The v-motion directive (src/directive/index.ts) is a thin adapter over useMotion: on created it resolves variants from binding value or node props, instantiates useMotion with visibility hooks deferred, and stashes the resulting MotionInstance on the DOM element; mounted then registers IntersectionObserver-based visibility hooks so SSR markup isn’t disturbed before hydration. MotionPlugin (src/plugin/index.ts) is the composition root: it registers the base directive, auto-registers a v-motion-{preset} directive per built-in preset, registers user-supplied custom directives, and registers the <Motion>/<MotionGroup> components. A flat motionState map keyed by directive binding value backs useMotions, letting multiple directive-driven instances be referenced imperatively elsewhere in an app. useMotionProperties is the shared core that every entry point ultimately depends on.

Tech Stack Runtime dependencies are @vueuse/core/@vueuse/shared (^13) for base reactivity helpers, popmotion (^11) as the physics/tween animation engine, framesync (^6) for rAF-batched value writes, and style-value-types (^5) for CSS value parsing; vue itself is a peer dependency (>=3.0.0), keeping the package framework-version-agnostic within Vue 3. The Nuxt integration (src/nuxt) is a standard Nuxt 3 module built with @nuxt/kit/@nuxt/module-builder. Build tooling is unbuild (dual ESM output with .d.mts types) plus nuxt-module-build for the Nuxt submodule; linting runs through @antfu/eslint-config via flat config, and the monorepo uses pnpm workspaces with playgrounds/ (vite, nuxt, vite-ssg) as manually-run integration testbeds. CI runs on GitHub Actions; releases are cut with bumpp and changelogs generated via gh-changelogen, including a nightly release channel (vueuse-motion-nightly).

Code Quality Test files cover the core public surface — useMotion, useMotionTransitions, useElementStyle, useElementTransform, reactiveStyle, reactiveTransform, isMotionInstance, and the <Motion>/<MotionGroup> components — run with Vitest plus @vue/test-utils and happy-dom, including a stored snapshot for transform-string output. Coverage is tracked via @vitest/coverage-v8 but no numeric threshold is enforced in CI. The codebase is fully TypeScript with exported types under src/types/, consistent useX composable naming matching VueUse conventions, and no obvious swallowed-error patterns in the reviewed files — options default to sensible empty values rather than throwing. Linting is enforced via @antfu/eslint-config with lint-staged/yorkie pre-commit hooks.

API Design The core ergonomic win is that useMotion/v-motion mirror Framer Motion’s variants model almost directly, lowering the learning curve for anyone coming from the React ecosystem — variants are plain reactive objects (initial, enter, visible, hovered, custom names) and transitions are inferred rather than manually wired. Getting started requires only app.use(MotionPlugin) plus a v-motion attribute with :initial/:enter bindings, and the 20+ presets let a consumer add scroll animations with a single directive name and no JS at all. The escape hatch to lower-level composables (useMotionProperties, useSpring, reactiveStyle) is layered on the same primitives rather than a separate parallel API, and TypeScript generics (MotionVariants<T>) give autocomplete on variant names. A dedicated docs site is built from the docs/ directory with worked examples per feature, beyond what the README alone covers.

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

Search