# Inertia > Declarative animation primitives for React Native, built on react-native-reanimated. Inspired by Framer Motion (web) and react-spring (cross-platform). DX-first — animations are props on a component, not imperative shared values, worklets, and `useAnimatedStyle` boilerplate. ## Install ```sh pnpm add @onlynative/inertia react-native-reanimated ``` Enable the Reanimated Babel plugin per its install guide. ## Imports ```ts import { Motion, Presence, MotionConfig, useGesture, useVariants, useMotionValue, useAnimation, useSpring, useTransform, useScroll, } from '@onlynative/inertia' // or for tree-shaking: import { MotionView } from '@onlynative/inertia/view' import { MotionText } from '@onlynative/inertia/text' import { MotionImage } from '@onlynative/inertia/image' import { MotionPressable } from '@onlynative/inertia/pressable' import { MotionScrollView } from '@onlynative/inertia/scroll-view' ``` ## Public API - `Motion.View` / `Motion.Text` / `Motion.Image` / `Motion.Pressable` / `Motion.ScrollView` — animatable primitives. Per-primitive style inference (no shared `ViewStyle & TextStyle & ImageStyle` fallback). - `` — mount / unmount transitions; children need explicit `key`s. Exiting children get `pointerEvents: 'none'` automatically. - `` — gates motion against the OS reduce-motion setting (default `"user"`). - `useGesture(transition?)` — hook-form of the `gesture` prop. Returns 0↔1 progress shared values (`pressed`, `focused`, `focusVisible`, `hovered`) plus a `handlers` bag to spread on a `Pressable`. Use when one gesture needs to drive multiple animated siblings (focus rings, MD3 state-layer halos, multi-element compositions). - `useVariants(variants, initial?)` — returns `{ current, transitionTo }` controller for the `controller` prop. - `useMotionValue(initial)` — thin pass-through over `useSharedValue`. Returns a `SharedValue` directly so it interops with every Reanimated API without unwrapping. - `useAnimation(target, transition?)` — drive a `SharedValue` toward `target` with any `TransitionConfig` (spring / timing / decay / no-animation, plus `repeat`). The general-purpose value-layer hook for boolean-state progress, indeterminate loops, and anywhere the same value needs to feed multiple `useAnimatedStyle` blocks. - `useSpring(target, config?)` — spring-only shorthand. Animates a `SharedValue` toward `target` with react-spring vocab. `target` may be a plain number (effect-driven) or a `SharedValue` (UI-thread reaction); the latter is the gesture-smoothing path. - `useTransform(value, inputRange, outputRange, options?)` / `useTransform(transformer)` — interpolate a numeric shared value onto a number or color range, or derive any value from any number of shared values via a worklet. Non-worklet transformers are auto-wrapped. - `useScroll()` — returns `{ scrollX, scrollY, onScroll }` for use with `Motion.ScrollView`. Scroll events fire on the UI thread. - `createMotionComponent(C)` — wrap any component with the same Motion prop surface, inferring style from `C`. ## Motion props `initial` (mount-only, non-reactive after first render — pass `false` to skip), `animate`, `exit`, `variants`, `controller`, `gesture`, `transition`, `layout`, `onAnimationEnd`. `layout` accepts `true` (default spring) or a `TransitionConfig` (spring / timing) and animates position + size changes that come from outside the `animate` flow (siblings reordering, dimensions toggling). `'decay'` downgrades to spring; `'no-animation'` and reduced motion both skip the animation. ## Transitions | `type` | Public config | Default? | | ---------------- | ---------------------------------------------------------------- | -------- | | `'spring'` | `tension`, `friction`, `mass`, `velocity`, `delay`, `repeat` | yes | | `'timing'` | `duration`, `easing`, `delay`, `repeat` | | | `'decay'` | `velocity`, `deceleration`, `clamp`, `delay` | | | `'no-animation'` | — | | Spring uses react-spring vocabulary (`tension`, `friction`, `mass`). Reanimated's raw `stiffness` / `damping` never appear in the public API. Per-property `transition` entries override the top-level config. ## Sequences and repeat ```tsx ``` `repeat`: `number` (finite, alternating) | `'infinite'` | `{ count, alternate }`. ## Gestures ```tsx ``` Sub-states layer **additively** in priority order: `hovered → focused → focusVisible → pressed`. Each declared sub-state owns an independent progress (0↔1) that fades in/out with its own transition; the worklet composites layers via `lerp` (numerics) / `interpolateColor` (colors). MD3 release-while-hovered behaves correctly — the press layer fades out without disturbing the hover layer. Per-layer transitions via `transition.`: ```tsx transition={{ pressed: { type: 'timing', duration: 50 }, hovered: { type: 'timing', duration: 90 }, }} ``` `focusVisible` engages only on keyboard focus (W3C `:focus-visible`) so click-focus on web doesn't flash a ring; on native it tracks `focused`. `hovered` is a no-op on native. ## Caveats - `Motion.Pressable` does **not** support function-form `style={({ pressed }) => ...}`. Reanimated's animated-component wrapper silently drops it. Drive press/focus/hover styling through `gesture` instead, or compute conditional styles once in render. - `initial` is read once on mount and intentionally non-reactive. To reset after a state change, change the component `key`, remount via ``, or drive the value through a controller. Pass `initial={false}` to skip the initial-mount animation. ## Animatable properties (alpha) Numeric: `opacity`, `translateX`, `translateY`, `scale`, `scaleX`, `scaleY`, `rotate`, `rotateX`, `rotateY`, `width`, `height`, `borderRadius`. Rotation values are degrees; the factory wraps them as `'${value}deg'`. `rotateX` / `rotateY` need a sibling `perspective` style entry to render in 3D. Color: `backgroundColor`, `borderColor`, `color`, `tintColor` (Image only). Hex, `rgb()` / `rgba()`, `hsl()` / `hsla()`, and named colors all work; the target string is forwarded straight through `withSpring` / `withTiming` and Reanimated handles RGBA interpolation natively. Auto-layout transitions ship via the `layout` prop (`true` / `TransitionConfig`) on every `Motion.*` primitive — see Layout. Shared element transitions (`layoutId`) are deferred: Reanimated 4 dropped the `sharedTransitionTag` API the previous design relied on. ## Optional adapter packages - `@onlynative/inertia-gradients` — `MotionLinearGradient` over `expo-linear-gradient`. Animatable: `colors`, `start`, `end`, `locations`. - `@onlynative/inertia-svg` — `MotionPath` over `react-native-svg`. Animatable: `d` (path morphing on structurally-compatible paths), `fill`, `stroke`, `strokeWidth`, opacities, `strokeDashoffset`. Source and target paths must share the same command sequence after implicit-repeat expansion; remount with `key` to switch shape. - `@onlynative/inertia-gestures` — `useDrag`, `useSwipe`, `usePan` over `react-native-gesture-handler`. ## Docs - Full docs: https://onlynative.github.io/inertia/ - Per-page LLM reference: https://onlynative.github.io/inertia/llms-full.txt - Source: https://github.com/onlynative/inertia