Skip to main content

Installation

Inertia is a thin wrapper around react-native-reanimated. The Reanimated install must complete first — Inertia is a peer to it, not a replacement.

Prerequisites​

  • Expo SDK 57 (React Native 0.86.x, React 19.2.3)
  • react-native-reanimated >=4.5.0 <4.6.0
  • react-native-worklets >=0.10.0 <0.11.0 — the worklet runtime Reanimated 4 is built on. It is a peer of both Reanimated and Inertia, so Yarn and pnpm will not install it for you.

Install Reanimated 4.5.3 or later in a development build or a store build. Reanimated 4.5.0 to 4.5.2 can show a view at its initial value after its animation ends: native keeps a settled value for 2 s only, and React holds the first-render value. When the JS thread does not read the settled value in that window (a stall of more than 1 s, a paused app, a native dialog), the next React commit writes the initial value back, so an entrance from opacity: 0 goes blank with no error. Reanimated 4.5.3 keeps the value until React has it (software-mansion/react-native-reanimated#9987). The fix is native, so it needs a new build, not an over-the-air update. Expo SDK 57 pins 4.5.1: add react-native-reanimated to expo.install.exclude in package.json, or expo install --check moves it back. Expo Go 57 runs 4.5.1 and cannot change, so a project in Expo Go keeps the defect.

Inertia targets one SDK band, not a floor, so SDK 57+ is not correct: the peer ranges stop below React Native 0.87 and below Reanimated 4.6, which the next SDK will carry.

The upper bounds are deliberate. Reanimated and react-native-worklets are version-locked to each other, and each pair works with one React Native band — Reanimated 4.5 needs worklets 0.10.x and React Native 0.83–0.86. A wider range would let a package manager pair a new Reanimated with an old worklets, a combination that cannot install at all. Supporting a second SDK needs a second release line, not a wider range.

Platforms​

PlatformStatus
iOSSupported
AndroidSupported
Web (react-native-web 0.21, through Expo)Supported. The example app and the docs demo run on web. Tests ran in Chromium only. Safari and Firefox are not tested.
macOS (react-native-macos)Not tested
Windows (react-native-windows)Not tested
ElectronNot tested. Expo does not supply an Electron target.

Inertia uses the same API on every supported platform. For web, add the web dependencies to an Expo app:

npx expo install react-dom react-native-web

Some behavior is different on web:

  • gesture.hovered operates on web only. On iOS and Android it does nothing. See Gestures.
  • layoutId measures the DOM rect with getBoundingClientRect() on web. See Layout.
  • A static web export writes each element at its initial values. Without the entrance guard, the page stays blank when the JavaScript bundle does not run. See Static export.

The adapter packages use the web support of their peer dependency:

  • @rootnative/inertia-gestures operates on web through the web support of react-native-gesture-handler.
  • @rootnative/inertia-svg operates on web through the web support of react-native-svg.
  • @rootnative/inertia-gradients operates on web through the web component of expo-linear-gradient. On web, Reanimated can log a console error on each frame for this component. The gradient renders and animates correctly.

Which package has what​

Read this before you install. The core package does not contain the gesture hooks, the gradient primitive, or the SVG primitives — each is a separate install. A swipe deck is one of the most common reasons to reach for this library, and useSwipe is not in the core package.

You wantPackageInstall
Motion.* primitives, Presence, MotionConfig, every value-layer hook@rootnative/inertiacore, always required
useDrag, useSwipe, usePan@rootnative/inertia-gestures+ react-native-gesture-handler
Animatable linear gradients@rootnative/inertia-gradients+ expo-linear-gradient
Animatable SVG paths and shapes@rootnative/inertia-svg+ react-native-svg

useTouchDrag is the exception: it is a drag hook that lives in core, under the @rootnative/inertia/touch subpath, because PanResponder is built into React Native and needs no extra peer dependency. Reach for it when you want drag without taking react-native-gesture-handler, or when the drag needs keyboard accessibility.

Versions​

All four packages ship in lockstep — the same version number, released together. Install the same version of every @rootnative/inertia* package you use, and each adapter requires a core version at or above its own.

Two things that mislead here:

  • The @rootnative/core and @rootnative/components packages of RootNative UI are a different library on a different version line. That family being on 0.0.0-alpha.x while this one is on 0.0.x is expected, not a mismatch.
  • npm view shows an alpha dist-tag on these packages that is older than latest. It is a leftover from the prerelease line. Follow latest.

Install​

Yarn is the default. Switch tabs for npm, pnpm, or Bun — your selection persists across the docs.

yarn add @rootnative/inertia react-native-reanimated react-native-worklets

Then enable the worklets Babel plugin per the Reanimated install guide. The plugin is what transforms 'worklet'-marked functions into UI-thread code; without it, every animation crashes at runtime.

A typical babel.config.js for a managed Expo app:

module.exports = function (api) {
api.cache(true)
return {
presets: ['babel-preset-expo'],
plugins: ['react-native-worklets/plugin'],
}
}

The worklets plugin must be last in the plugins array. (react-native-reanimated/plugin still works in Reanimated 4 — it is a one-line shim that forwards to react-native-worklets/plugin — but the worklets name is the canonical one, and it's the one Inertia's own install check tells you to add.)

The plugin must run on node_modules too​

@rootnative/inertia, react-native-reanimated and react-native-worklets all ship their 'worklet' directives untransformed. The plugin in the consumer's build turns them into UI-thread code. Metro runs Babel on node_modules, so an Expo or bare React Native app needs nothing more than the config above.

A web bundler without Metro (Vite, webpack, Electron, Tauri) skips node_modules by default. The build then succeeds, and no animation plays: every animated style stays at its first frame, and a production build shows no error. Inertia logs one console.error at the first Motion render in that case, in production too. Add the three packages to the plugin's include list. A Vite example with @rolldown/plugin-babel:

babel({
include: [
/src\/.*\.tsx?$/,
/node_modules\/(@rootnative|react-native-reanimated|react-native-worklets)\/.*\.m?js$/,
],
plugins: ['react-native-worklets/plugin'],
})

The equivalent for babel-loader in webpack is an include array with the same paths.

First animation​

import { Motion } from '@rootnative/inertia'

export function FadeIn() {
return (
<Motion.View
initial={{ opacity: 0, translateY: 20 }}
animate={{ opacity: 1, translateY: 0 }}
transition={{
opacity: { type: 'timing', duration: 200 },
translateY: { type: 'spring', tension: 180, friction: 12 },
}}
/>
)
}

Try it live​

The example app runs in the iframe below — tap any screen to interact with the same primitives you'd ship in your app.

example / view

Subpath imports​

Each primitive is reachable directly so apps that animate one element don't pull in the rest:

import { MotionView } from '@rootnative/inertia/view'
import { MotionText } from '@rootnative/inertia/text'
import { MotionImage } from '@rootnative/inertia/image'
import { MotionPressable } from '@rootnative/inertia/pressable'
import { MotionScrollView } from '@rootnative/inertia/scroll-view'

@rootnative/inertia is sideEffects: false, so a bundler with tree-shaking enabled (Metro 0.79+, Webpack, Rollup) will drop primitives you don't reference. The named imports above tree-shake to within ~0.1 kB of the subpath equivalents, verified per-primitive in CI.

One caveat: the Motion namespace object cannot tree-shake. import { Motion } from '@rootnative/inertia' followed by Motion.View holds the whole namespace live (it's a literal object — bundlers can't eliminate property accesses). Use it for ergonomics; reach for import { MotionView } from '@rootnative/inertia' (or the subpath) when bundle size matters.

Optional adapter packages​

Three sibling packages extend Inertia for capabilities that need extra peer dependencies. The core library has no required dependency on any of them — install only what you need.

Gestures (@rootnative/inertia-gestures)​

Drag / pan / swipe hooks built on react-native-gesture-handler. Compose with any Motion.* primitive via <GestureDetector>.

yarn add @rootnative/inertia-gestures react-native-gesture-handler

Then follow the react-native-gesture-handler install guide — it needs <GestureHandlerRootView> near the root of your app. See Gestures adapter for the hooks.

Gradients (@rootnative/inertia-gradients)​

MotionLinearGradient wraps expo-linear-gradient with animatable colors / start / end / locations. Works in bare React Native as well as Expo.

yarn add @rootnative/inertia-gradients expo-linear-gradient

See Gradients for the primitive.

SVG (@rootnative/inertia-svg)​

MotionPath wraps react-native-svg's <Path> with animatable d (path morphing on structurally-compatible paths), fill, stroke, strokeWidth, opacities, and strokeDashoffset. Works in bare React Native as well as Expo.

yarn add @rootnative/inertia-svg react-native-svg

See SVG for the primitive.

Reduced motion​

Inertia respects the OS reduce-motion accessibility setting by default — no extra wiring needed. To override the default for the whole app or a subtree, see MotionConfig.