Star203

Documentation

Configuration

Define animation tokens, presets, diagnostics, reduced-motion policy, plugins, and the adapter target in one typed file.

Configuration

Motionwind uses a single configuration file at your project root. It defines animation tokens, reusable presets, strict diagnostics, and accessibility policy.

The config file

Create motionwind.config.ts at your project root:

import { defineConfig } from "motionwind-core";

export default defineConfig({
  adapter: "react",
  strict: true,
  reducedMotion: "user",
  tokens: {
    durations: { fast: 160, deliberate: 480 },
    easings: { product: [0.22, 1, 0.36, 1] },
    springs: { responsive: { stiffness: 420, damping: 28 } },
  },
  presets: {
    "card-enter":
      "animate-initial:opacity-0 animate-enter:opacity-100 animate-duration-deliberate",
  },
});

Configuration options

adapter

Which framework you're using. Determines how the compiler and runtime behave.

adapter: "react" | "vue" | "vanilla" | "react-native";

strict

When true, unknown animation classes and invalid syntax produce errors instead of warnings. Recommended for CI.

strict: true;

What strict catches:

  • Unknown gesture prefixes (e.g., animate-invalid:scale-105)
  • Unknown property names (e.g., animate-hover:invalid-prop)
  • Invalid extension syntax
  • Unknown preset names

reducedMotion

Controls what happens when the user's OS prefers reduced motion.

reducedMotion: "user" | "always" | "never";
ValueBehavior
"user"Respect the OS prefers-reduced-motion setting
"always"Always disable animations regardless of OS preference
"never"Never reduce motion regardless of OS preference

Always use "user" unless you have a documented reason to override it.

Tokens

Tokens are named values you reference in classes. They keep your animation vocabulary consistent across the codebase.

Duration tokens

Map short names to millisecond values:

tokens: {
  durations: {
    fast: 160,        // animate-duration-fast → 160ms
    normal: 240,      // animate-duration-normal → 240ms
    deliberate: 480,  // animate-duration-deliberate → 480ms
    slow: 720,        // animate-duration-slow → 720ms
  },
}

Usage:

<button class="animate-hover:scale-105 animate-duration-fast">Fast</button>
<article class="animate-enter:opacity-100 animate-duration-deliberate">
  Slow
</article>

Easing tokens

Map names to cubic-bezier curves or easing functions:

tokens: {
  easings: {
    product: [0.22, 1, 0.36, 1],    // custom cubic-bezier
    "ease-in-out": "ease-in-out",    // named easing
  },
}

Usage:

<div class="animate-hover:x-10 animate-ease-product">Custom easing</div>

Spring tokens

Map names to spring physics configurations:

tokens: {
  springs: {
    responsive: { stiffness: 420, damping: 28 },  // fast response
    bouncy: { stiffness: 300, damping: 10 },       // bouncy feel
    gentle: { stiffness: 120, damping: 20 },       // gentle settle
  },
}

Usage:

<button class="animate-hover:scale-105 animate-spring-responsive">
  Responsive
</button>
<div class="animate-tap:scale-95 animate-spring-bouncy">Bouncy</div>

Presets

Presets are reusable animation classes defined as a string of animate-* tokens:

presets: {
  "card-enter":
    "animate-initial:opacity-0 animate-enter:opacity-100 animate-duration-deliberate",
  "button-press":
    "animate-tap:scale-95 animate-spring-responsive",
  "list-stagger":
    "animate-initial:opacity-0 animate-initial:y-10 animate-enter:opacity-100 animate-enter:y-0 animate-stagger-50",
}

Usage:

<article class="animate-preset-card-enter">Fades in on mount</article>
<button class="animate-preset-button-press">Presses on tap</button>

Passing config to frameworks

Each framework receives the config differently.

Next.js

// next.config.mjs
import { withMotionwind } from "motionwind-react/next";
import motionwindConfig from "./motionwind.config";

export default withMotionwind({}, motionwindConfig);

React + Vite

// vite.config.ts
import { motionwind } from "motionwind-react/vite";
import motionwindConfig from "./motionwind.config";

export default defineConfig({
  plugins: [motionwind(motionwindConfig), react()],
});

Vue + Vite

// vite.config.ts
import {
  createMotionwindTransform,
  MotionwindPlugin,
} from "motionwind-vue/vite";
import motionwindConfig from "./motionwind.config";

export default defineConfig({
  plugins: [
    createMotionwindTransform(motionwindConfig),
    vue(),
    MotionwindPlugin(motionwindConfig),
  ],
});

Vanilla JavaScript

import { motionwind } from "motionwind-vanilla";
import motionwindConfig from "./motionwind.config";

motionwind({ config: motionwindConfig });

React Native / Expo

import { MotionwindProvider } from "motionwind-react-native";
import motionwindConfig from "./motionwind.config";

<MotionwindProvider config={motionwindConfig}>
  <App />
</MotionwindProvider>;

Plugins

Plugins extend Motionwind with custom properties, presets, and diagnostics:

import { defineMotionwindPlugin } from "motionwind-core";

const myPlugin = defineMotionwindPlugin({
  name: "my-plugin",
  version: "1.0.0",
  core: ">=2 <3",
  presets: {
    "my-enter": "animate-enter:opacity-100 animate-duration-fast",
  },
  definitions: [
    {
      id: "my.custom",
      category: "property",
      label: "custom-",
      snippet: "custom-${1:100}",
      description: "A custom property.",
    },
  ],
});

Add plugins to your config:

export default defineConfig({
  adapter: "react",
  plugins: [myPlugin],
});

Full example

Here's a complete config for a React project:

import { defineConfig } from "motionwind-core";

export default defineConfig({
  adapter: "react",
  strict: true,
  reducedMotion: "user",
  tokens: {
    durations: {
      fast: 160,
      normal: 240,
      deliberate: 480,
    },
    easings: {
      product: [0.22, 1, 0.36, 1],
    },
    springs: {
      responsive: { stiffness: 420, damping: 28 },
      bouncy: { stiffness: 300, damping: 10 },
    },
  },
  presets: {
    "card-enter":
      "animate-initial:opacity-0 animate-enter:opacity-100 animate-duration-deliberate",
    "button-press": "animate-tap:scale-95 animate-spring-responsive",
  },
});

This gives you these classes:

ClassResolves to
animate-duration-fasttransition.duration: 0.16
animate-ease-producttransition.ease: [0.22, 1, 0.36, 1]
animate-spring-responsivetransition: { type: "spring", stiffness: 420, damping: 28 }
animate-preset-card-enterFades in from opacity 0 with deliberate timing
animate-preset-button-pressScales to 95% with responsive spring