Documentation

Introduction

motionwind lets you write Motion animations as Tailwind-like utility classes, compiled away at build time.

motionwind

motionwind brings the expressiveness of Motion (formerly Framer Motion) into the Tailwind CSS mental model. Instead of writing verbose JSX props, you declare animations as familiar utility classes directly in your className string.

A Babel plugin runs at build time, finds every animate-* class, parses them into Motion props, and rewrites the element as a motion.* component -- all before your code ever reaches the browser.

<div className="animate-hover:scale-110 animate-tap:scale-90 animate-spring">
  Hover or tap me
</div>

At build time, that single line becomes:

<motion.div
  whileHover={{ scale: 1.1 }}
  whileTap={{ scale: 0.9 }}
  transition={{ type: "spring" }}
>
  Hover or tap me
</motion.div>

You never import motion, you never write prop objects, and the transform disappears from your production bundle.

Why motionwind?

Zero runtime overhead

The Babel plugin does all the work at compile time. There is no extra runtime parser shipping with your app. The animate-* classes are consumed entirely by the transform and do not appear in the final CSS or HTML output. What ships to the browser is plain Motion code.

Familiar syntax

If you know Tailwind, you already know motionwind. The class pattern is animate-{gesture}:{property}-{value}. Gestures like hover, tap, focus, and inview map directly to Motion's whileHover, whileTap, whileFocus, and whileInView. Properties like scale, rotate, x, y, and opacity work exactly as you would expect.

Colocation

Animations live right next to the styles they modify. No separate animation files, no variant objects to define elsewhere. A single className string describes the element's visual appearance, layout, and motion behavior.

Full Motion power

motionwind supports gestures, scroll-triggered animations, enter/exit transitions, spring physics, drag, viewport detection, repeating animations, and arbitrary Motion properties via bracket syntax. Anything you can do with Motion, you can express as a class.

Core concepts

Gesture prefixes

Every animation class starts with animate- followed by a gesture prefix and a colon:

  • animate-hover: -- runs while the pointer is over the element
  • animate-tap: -- runs while the element is pressed
  • animate-focus: -- runs while the element has keyboard focus
  • animate-inview: -- runs when the element enters the viewport
  • animate-drag: -- runs while the element is being dragged
  • animate-initial: -- the starting state before the enter animation
  • animate-enter: -- the target state for mount/entrance animations
  • animate-exit: -- the state to animate to when the element unmounts

Animatable properties

After the gesture prefix, you specify a property and value:

  • Transform: scale, scale-x, scale-y, rotate, rotate-x, rotate-y, skew-x, skew-y, x, y
  • Visual: opacity, blur, brightness, contrast, saturate
  • Layout: w (width), h (height), rounded (border-radius)

Transition configuration

Global transition settings are written without a gesture prefix:

  • animate-duration-300 -- 300ms duration
  • animate-ease-in-out -- easing function
  • animate-spring -- spring physics
  • animate-stiffness-200 -- spring stiffness
  • animate-repeat-3 -- repeat 3 times

Dynamic classNames

The Babel plugin only transforms static string literals. For dynamic or conditional classNames, motionwind provides a runtime component API:

import { mw } from "motionwind-react";

<mw.button className={`animate-hover:scale-110 ${isActive ? "bg-blue-500" : "bg-gray-500"}`}>
  Click
</mw.button>

Next steps