# motionwind-react-native — Complete Documentation for LLMs ## What is motionwind-react-native? motionwind-react-native brings Tailwind-like animation classes to React Native, powered by react-native-reanimated. Write `animate-tap:scale-95` and it runs a 60fps native animation via Reanimated shared values. Runtime parsing, no Babel step needed. ## Installation ### Prerequisites - React 18+ or 19+ - React Native 0.72+ - react-native-reanimated 3.x or 4.x - Node.js 18+ ### Expo Setup ```bash npx expo install react-native-reanimated react-native-gesture-handler npm install motionwind-react-native ``` ### Bare React Native Setup ```bash npm install motionwind-react-native react-native-reanimated react-native-gesture-handler ``` ### Babel Config (required for Reanimated) ```js // babel.config.js module.exports = function (api) { api.cache(true); return { presets: ["babel-preset-expo"], plugins: ["react-native-reanimated/plugin"], // must be last }; }; ``` Clear cache after setup: `npx expo start -c` ### Optional: NativeWind ```bash npm install nativewind tailwindcss ``` Non-animation Tailwind classes (e.g., `bg-blue-500`, `p-4`) pass through to NativeWind automatically. ## How It Works The `mw.*` component proxy: 1. Parses `className` for `animate-*` tokens at runtime (LRU cached, max 1000 entries) 2. Creates Reanimated shared values for each animated property 3. Maps gesture prefixes to RN event handlers (`animate-tap:` → `onPressIn`/`onPressOut`) 4. Passes non-animation classes to NativeWind via `className` 5. When no `animate-*` classes exist, renders a plain (non-animated) RN component ## API ### mw.* Components ```tsx import { mw } from "motionwind-react-native"; ``` Available: `mw.View`, `mw.Text`, `mw.Image`, `mw.Pressable`, `mw.ScrollView`, `mw.FlatList`, `mw.TextInput`, `mw.TouchableOpacity`, `mw.SafeAreaView` Any unknown key (e.g., `mw.MyCustom`) creates an animated component wrapping `View`. ### useMotionwind(className) ```tsx import { useMotionwind } from "motionwind-react-native"; const { animatedStyle, handlers, parsed, animateTo, resetToBase } = useMotionwind(className); ``` Returns: - `animatedStyle` — Reanimated `useAnimatedStyle` result - `handlers` — Object with `onPressIn`, `onPressOut`, `onHoverIn`, `onHoverOut`, `onFocus`, `onBlur` - `parsed` — Full `ParsedResult` from parsing - `animateTo(style)` — Manually animate to a gesture state - `resetToBase()` — Reset to "animate" (enter) state ### useInView() ```tsx import { useInView } from "motionwind-react-native"; const { isInView, onLayout, viewRef } = useInView(); ``` ### parseMotionClasses(className) ```tsx import { parseMotionClasses } from "motionwind-react-native"; const result = parseMotionClasses("bg-blue-500 p-4 animate-tap:scale-95 animate-duration-200"); // Returns: { nativewindClasses, gestures, transition, viewport, drag, hasMotion } ``` ### clearParserCache() ```tsx import { clearParserCache } from "motionwind-react-native"; clearParserCache(); ``` ## Syntax Reference ### Format ``` animate-{gesture}:{property}-{value} (for animations) animate-{config}-{value} (for transition/viewport/drag config) ``` ### Gesture Prefixes | Prefix | Maps to | RN handler | Triggers when | |---|---|---|---| | `initial` | initial state | — | Before mount | | `enter` | animate state | — | On mount | | `exit` | exit state | — | On unmount | | `tap` | whileTap | onPressIn/onPressOut | Element pressed | | `hover` | whileHover | onHoverIn/onHoverOut | Pointer enters (RN 0.71+) | | `focus` | whileFocus | onFocus/onBlur | Keyboard focus | | `inview` | whileInView | useInView | Enters viewport | ### All Animatable Properties #### Transforms | Class pattern | RN property | Value handling | |---|---|---| | `scale-{n}` | `scale` | n/100 (110 → 1.1) | | `scale-x-{n}` | `scaleX` | n/100 | | `scale-y-{n}` | `scaleY` | n/100 | | `rotate-{n}` | `rotate` | Becomes `"{n}deg"` string | | `rotate-x-{n}` | `rotateX` | Becomes `"{n}deg"` string | | `rotate-y-{n}` | `rotateY` | Becomes `"{n}deg"` string | | `skew-x-{n}` | `skewX` | Becomes `"{n}deg"` string | | `skew-y-{n}` | `skewY` | Becomes `"{n}deg"` string | | `x-{n}` | `translateX` | Pixels (plain number) | | `y-{n}` | `translateY` | Pixels (plain number) | #### Visual Properties | Class pattern | RN property | Value handling | |---|---|---| | `opacity-{n}` | `opacity` | n/100 (0-100 → 0-1) | | `bg-{color}` | `backgroundColor` | #hex, rgb(), rgba(), hsl(), hsla() | | `text-{color}` | `color` | #hex, rgb(), rgba(), hsl(), hsla() | | `border-{color}` | `borderColor` | #hex, rgb(), rgba(), hsl(), hsla() | #### Layout Properties | Class pattern | RN property | |---|---| | `w-{n}` | `width` | | `h-{n}` | `height` | | `rounded-{n}` | `borderRadius` | | `border-w-{n}` | `borderWidth` | | `p-{n}` | `padding` | | `m-{n}` | `margin` | | `gap-{n}` | `gap` | | `top-{n}` | `top` | | `left-{n}` | `left` | | `right-{n}` | `right` | | `bottom-{n}` | `bottom` | #### Typography | Class pattern | RN property | |---|---| | `text-size-{n}` | `fontSize` | | `tracking-{n}` | `letterSpacing` | | `leading-{n}` | `lineHeight` | #### Keyframe Arrays `{property}-[v1,v2,v3]` — e.g., `scale-[100,120,100]` → `scale: [1, 1.2, 1]` ### Negative Values Prefix with `-` after the colon: `animate-tap:-rotate-45` → `rotate: "-45deg"` ### Value Suffixes | Suffix | Maps to | |---|---| | `px` | Plain number (device-independent pixels) | | `pct` | Percentage string (e.g., `"50%"`) | | (none) | Plain number | Note: CSS-only units (`vh`, `vw`, `rem`, `em`) are NOT supported in React Native. ## Transition Configuration All transition classes have NO gesture prefix: | Class | Effect | |---|---| | `animate-duration-{ms}` | Duration in milliseconds | | `animate-delay-{ms}` | Start delay in milliseconds | | `animate-delay-children-{ms}` | Delay all children | | `animate-spring` | Use spring animation | | `animate-stiffness-{n}` | Spring stiffness | | `animate-damping-{n}` | Spring damping | | `animate-mass-{n}` | Spring mass (n/10) | | `animate-ease-linear` | Linear easing | | `animate-ease-in` | Ease in | | `animate-ease-out` | Ease out | | `animate-ease-in-out` | Ease in-out | | `animate-ease-circ-in` | Circular ease in | | `animate-ease-circ-out` | Circular ease out | | `animate-ease-circ-in-out` | Circular ease in-out | | `animate-ease-back-in` | Overshoot ease in | | `animate-ease-back-out` | Anticipation ease out | | `animate-ease-back-in-out` | Both | | `animate-ease-[x1,y1,x2,y2]` | Custom cubic-bezier | | `animate-repeat-{n}` | Repeat N times | | `animate-repeat-infinite` | Loop forever | | `animate-repeat-reverse` | Alternate direction each repeat | | `animate-stagger-{ms}` | Stagger children by duration | | `animate-stagger-reverse` | Reverse stagger direction | ## Viewport Configuration (for inview gesture) | Class | Effect | |---|---| | `animate-once` | Trigger once then lock | | `animate-amount-{n}` | Trigger at n% visibility (0-100) | | `animate-amount-all` | Trigger when fully visible | | `animate-margin-{n}` | Add margin threshold for trigger | ## Drag Configuration | Class | Effect | |---|---| | `animate-drag-x` | Draggable on X-axis | | `animate-drag-y` | Draggable on Y-axis | | `animate-drag-both` | Draggable in both directions | | `animate-drag-elastic-{n}` | Elastic coefficient (n/100) | | `animate-drag-snap` | Snap to origin on release | | `animate-drag-no-momentum` | Disable momentum | | `animate-drag-constraint-t-{n}` | Top bound | | `animate-drag-constraint-b-{n}` | Bottom bound | | `animate-drag-constraint-l-{n}` | Left bound | | `animate-drag-constraint-r-{n}` | Right bound | ## Web-Only Properties (Silently Dropped) These classes are parsed but produce no output on native: - `filter-*`, `blur-*`, `brightness-*`, `contrast-*`, `saturate-*` - `backdrop-blur-*` - `clip-*` - `path-length-*`, `path-offset-*`, `path-spacing-*` (SVG) - `box-shadow` / `shadow-*` ## Common Patterns ### Mount Animation (fade + slide up) ```tsx Fades in on mount ``` ### Tap Interaction ```tsx Press me ``` ### Spring Button ```tsx Springy ``` ### Infinite Pulse ```tsx Pulsing ``` ### Scroll Reveal ```tsx Scroll to reveal ``` ### Draggable Element ```tsx Drag me ``` ### Staggered Children ```tsx First Second ``` ### Color Animation ```tsx Tap for color ``` ## Web vs. Native Differences | Feature | Web (motionwind-react) | Native (motionwind-react-native) | |---|---|---| | Animation engine | Motion (Framer Motion) | react-native-reanimated | | Styling | Tailwind CSS | NativeWind (optional) | | Transform | Build-time Babel transform | Runtime parsing | | `whileHover` | Mouse hover | onHoverIn/Out (RN 0.71+) | | `whileTap` | Click/touch | onPressIn/Out | | CSS filters | blur(), brightness() | Not supported | | CSS units | vh, vw, rem, em | Plain numbers (dp) | | x/y translate | CSS translate | translateX/translateY | | Rotate values | Number (degrees) | String ("45deg") | | Duration unit | Seconds (0.3) | Milliseconds (300) | ## Peer Dependencies | Package | Version | Required | |---|---|---| | react | ^18.0.0 or ^19.0.0 | Yes | | react-native | >=0.72.0 | Yes | | react-native-reanimated | ^3.0.0 or ^4.0.0 | Yes | | nativewind | ^4.0.0 | Optional | ## Troubleshooting 1. **Animations not running?** — Confirm Reanimated Babel plugin is in babel.config.js and cache is cleared. 2. **Gestures not working?** — Wrap app with `` from react-native-gesture-handler. 3. **NativeWind classes not applying?** — NativeWind must be configured separately. Without it, use `style` prop. 4. **"Unable to resolve module"?** — Clear Metro cache: `npx expo start -c` or `npx react-native start --reset-cache`. 5. **Web-only properties (blur, backdrop-blur)?** — These are silently dropped on native. No equivalent in RN.