react-brain
Browse decisions
React & language foundations 3App architecture 10UI 13Platform & native 9Build, test, observe, secure 6AI in React apps 3stack composerdecision recordscensusstaleness bencharchitecturechangelogmethodologyroadmap

entriesui · verified 2026-08-18 · react + react-native

Animation & gestures

reviewedconfidence: mediumtrack: weakenedthis tier holds 67% on the public scorecard (6/19 graded · 0 overturned) →

cited by: rn-versions · svg

re-verified 8× — 2026-08-18 · 2026-08-07 · 2026-08-04 · 2026-07-28 · 2026-07-17 · 2026-07-16 +2 · changelog

recommendation

React Native → Reanimated 4 + Gesture Handler 3 for real UI-thread animation & gestures (core Animated only for trivial cases). Web (React DOM) → Motion (framer-motion's successor) for declarative layout/exit/gesture animation.

  • web animation (React DOM) → Motion / framer-motion — Reanimated & Gesture Handler are React Native only
  • custom/GPU drawing or visuals → react-native-skia
  • designer-made animations → lottie-react-native (or Skia/Skottie)
  • designer animations that need INTERACTIVITY/state machines, or many Rive views sharing files → Rive; new work goes to the Nitro line @rive-app/react-native (0.4.x, early — pin), not the quiet legacy package
  • image-to-image morph transition → react-native-morph-view (GPU shader; niche, lead-level)
  • simple fade/slide, want declarative → LayoutAnimation or a Reanimated entering/exiting preset (RN); Motion (web). Moti reads as the ergonomic choice here but is dormant — see the option row
  • custom screen / shared-element transitions on React Navigation → react-native-screen-transitions (new; pin)
  • hundreds of concurrently-animating views blowing the frame budget → benchmark react-native-ease (fully native-driven; young)
  • heavy animation perf → route to react-native-best-practices

Options & tradeoffs

the field considered — and why each one isn’t the default here

optiontradeoffevidence
Reanimated 4UI-thread + CSS-style animations/transitions; worklets split into react-native-worklets; requires New Arch6.3M/wk · ships in 12/34
Gesture Handler 3.0native gestures; v3 adds a hook-based API built for the React Compiler + Reanimated 46.2M/wk · ships in 12/34
react-native-worklets (0.11.x)the worklets runtime under Reanimated 4 (now its own package; npm latest 0.11.4, verified 2026-08-18 — an earlier '8.0' claim here was WRONG, caught by the firsthand watch); 0.11 adds experimental Hermes-bytecode worklets + Shareable values across isolated runtimes. 0.12.0 shipped 2026-08-11 but sits on the `next` dist-tag, NOT latest (WeakRef on worklet runtimes; Bundle Mode script loading on par with RN's own loaders) — read it as the next line, not the one to install. TURN ON BUNDLE MODE (stable since 0.10) — below RN 0.87 it is the fix for the Hermes V1 memory regression, and worth it regardless; see note5.6M/wk · ships in 10/34
react-native-skia (2.x)GPU 2D drawing; v2.1 plays Lottie via Skottie; pairs with Reanimated1.1M/wk · ships in 3/34
lottie-react-nativedesigner Lottie/JSON animations; v7 adds visionOS1.2M/wk · ships in 7/34
Rive — @rive-app/react-native (Nitro) / rive-react-native (legacy)designer-driven INTERACTIVE vector animation (state machines) — the Lottie alternative; the SDK was rewritten on Nitro under Rive's official scope (@rive-app/react-native, 0.4.x EARLY line): files/view-models become shared typed HybridObjects — Margelo-measured up to 94× multi-view loads + ~4.7× lower memory vs legacy (rewrite authors' own benchmark); legacy unscoped pkg at 9.8.3, quiet since 2026-0438k/wk
react-native-morph-view (Software Mansion)GPU image-morph transitions (Metal/AGSL shaders, metaball blend); RN 0.76+, iOS 15.1+/Android 13+; niche/new
Animated (core)built-in; RN 0.85 shared backend allows layout-prop animation with the native driver (from 0.85.1)6.3M/wk · ships in 12/34
Moti / LayoutAnimationdeclarative transitions; Moti wraps Reanimated — but it is DORMANT, not merely slow: npm 0.30.0 published 2025-01-29 and the repo has taken no commits since 2025-03-11 (verified 2026-08-18), while its own dependency list still pins framer-motion ^6.5.1 and its react-native-reanimated peer is a wildcard, so nothing warns you on Reanimated 4. Fine if already installed and working; do NOT start new work on it — plain Reanimated or LayoutAnimation is the live path379k/wk
react-native-screen-transitionscustom screen/shared-element transitions FOR React Navigation (blank-stack navigator, bounds-driven navigation.zoom(), gesture choreography); featured on the official React Navigation blog; v3.8 active 2026-07, newer93k/wk
react-native-easeanimations driven entirely by Core Animation/ObjectAnimator — no per-frame JS OR UI-thread worklet work; author-run benchmark shows it holding frame budget at view counts where Reanimated saturates; young, and see the benchmark-bias caveat in reading21k/wk
Motion / framer-motion (WEB)the web (React DOM) animation default — declarative layout/exit/gesture animation; Motion is framer-motion's successor; React Native uses Reanimated instead12.6M/wk · ships in 3/34

evidence: npm weekly downloads (signals snapshot) · “ships in n/D” = adoption across the production-app census, honest denominators

npm weekly downloads (from the corpus's last signals run): react-native-reanimated 6.3M · react-native-gesture-handler 6.2M · react-native-worklets 5.6M · framer-motion 37.5M · motion 12.6M · lottie-react-native 1.2M · @shopify/react-native-skia 1.1M · moti 379k · react-native-screen-transitions 93k · react-native-ease 21k · @rive-app/react-native 38k · rive-react-native 103k

Verified notes

RN 0.85 (2026-04) introduced a shared C++ animation backend so Animated and Reanimated ride one engine (built with Software Mansion). Heavy-animation perf routes to react-native-best-practices. RIVE (added 2026-07-13, Margelo blog pass): the RN SDK's Nitro rewrite ships as NEW scoped @rive-app/react-native (0.4.14) while legacy rive-react-native sits at 9.8.3 (both verified vs npm 2026-07-13) — a supersession in progress; WATCH for the new line reaching 1.0 or a formal legacy deprecation before adding a migrate rule (wired as tripwires 2026-07-16 — firsthand fires when either lands). WEB: Motion (the framer-motion successor) is the web animation default — added 2026-06-25 after the evidence-loop corpus self-audit flagged that this [react]-platform entry had options/recommend for React Native only, no web pick (caught from ledgerhr's framer-motion usage). Verified vs the official RN 0.85 blog. HERMES V1 WORKLET MEMORY REGRESSION — FIXED IN RN 0.87 (history, kept because it still bites every app below 0.87; original finding 2026-07-28, verified vs the Software Mansion writeup and reproduced independently in the Calazans benchmark below): Hermes V1 attached ~512KB of debug metadata to EVERY eval'd worklet, and Worklets' Legacy Eval Mode ships animation code to the UI runtime as strings — so memory scaled with unique worklets triggered. Software Mansion measured Expensify's bundle at 1,000+ unique worklets, with 100+ evaluated just to initialize (≥50MB at startup). The Hermes fix was backported to 250829098.0.15, and RN 0.87 (2026-08-11) pins hermes-compiler 250829098.0.16 — verified vs the v0.87.0 tag's package.json — so the regression is closed from 0.87 up. BELOW 0.87 the ladder still applies, in preference order: turn on WORKLETS BUNDLE MODE (exposes the mmap'd bytecode bundle instead of eval'ing strings — measured −~100MB PSS, ~45%, native heap ~142MB→~32MB, with frame rate and CPU unchanged), the experimental bytecode option for Legacy Eval Mode, or pin Hermes 250829098.0.15. TURN ON BUNDLE MODE REGARDLESS: SWM still recommends it for its own performance gains, and Worklets 0.12 (2026-08-11) brought its script loading to parity with React Native's own loaders — local bundles are mmapped instead of held in an in-memory std::string for the app's lifetime, and Android dev-server bundles stream to a cache file (verified vs the worklets-0.12.0 release). SKIA 2.10 MOVED OFF HostObject (2026-07-23, verified vs the release + the published manifest): the release's one feature is "migrate from host objects to native states" (#3964) — Skia's JSI objects now use NativeState rather than HostObject, the direction the Hermes team favors because HostObject routes every property access back through a C++ trampoline. Its published peer range is react-native >= 0.78 and react >= 19.0, with react-native-reanimated >= 4.0.0 and react-native-worklets >= 0.7.0 as OPTIONAL peers — i.e. Skia does not force Reanimated on you, but if you pair them the v4 line is the floor. MOTION 13 (web; 2026-08-06, verified vs npm + the motion.dev upgrade guide): one breaking change — drops @emotion/is-prop-valid as an auto-detected optional dependency, so styled-components/Emotion codebases must inject the prop-validation function via MotionConfig (or invert composition so the styling library controls prop forwarding) or previously-filtered props leak onto the DOM. framer-motion (the legacy package name) moved to 13.0.0 in lockstep; neither package is deprecated.

Canonical reading

Editorial annotations on why each piece matters — the articles themselves are the originals; read them there.

CSS vs. JavaScript AnimationsJosh W. Comeau

First-principles explainer on the real distinction (main-thread contention, compositor-friendly properties, WAAPI) rather than the CSS-is-always-faster myth. A durable animation mental model that applies to web and RN reasoning alike.

How react-native-ease runs animations with no JavaScript loopPeter Piekarczyk

Deep dive on driving animations entirely via Core Animation / ObjectAnimator with no per-frame JS, incl. iOS model vs presentation layers and cross-platform spring physics. The mental model for truly native-driven RN animation.

Chasing a Phantom Jump: making Skia + Reanimated smooth on low-end AndroidVishesh Raheja (Margelo)

A low-end-Android profiling deep-dive: smoothness is frame-cadence timing, not curve math; the dominant fix is SurfaceView over TextureView (no per-frame texture upload, ~50-65% less RenderThread CPU), plus stable-reference buffer mutation and quantizing per-frame values. With profiler-triangulation methodology (gfxinfo/Perfetto/Hermes).

The real cost of React Native animations: benchmarking every approachJanic Duplessis (Expo; react-native-ease author)

The only cross-library per-frame benchmark: UI-thread cost of core Animated vs Reanimated (shared values AND the CSS API) vs Ease on real devices — at ~500 animating views only the fully native-driven approach stays under frame budget (Reanimated SVs measured ~36ms/frame). CAVEAT: the author created Ease, the approach that wins — treat the numbers as vendor-run and re-measure on your own screens; the methodology and the per-frame cost model are the durable part.

Which React Native Animation Library Should You Use for Performance?Andrei Calazans

The INDEPENDENT replication the Expo/Ease benchmark above asks for — same three-way question (Animated with useNativeDriver, Reanimated 4.5, Ease 0.7), different author, reproducible harness (AndreiCalazans/react-native-animation-performance), Android release build on New Arch, 90Hz physical device, Maestro-driven, median of 3, 60 boxes animating. It splits the answer BY ANIMATION TYPE instead of picking a winner: for touch/state/loop all three keep JS at the idle floor and Ease drops the fewest frames with Animated close behind; SCRUB is the fork — Animated drives the value via PanResponder→setValue on the JS thread (29% JS CPU, ~26% of JS frames dropped, and useNativeDriver cannot save a value you set from JS each frame) while Reanimated keeps it entirely on the UI thread (2.9% JS CPU). Blunt version: Reanimated when a finger is dragging, Ease or the native driver otherwise. Two caveats the author states himself: 60 independent nodes deliberately punishes per-node cost (Reanimated's useAnimatedStyle mapper), so the UI-thread loop ranking is partly an artifact — at one hero element Reanimated is likely best overall; and the ~50MB memory premium it measured DISAPPEARS with Worklets Bundle Mode on (−~100MB PSS in the follow-up re-run).

A React trick to improve exit animationsMax Barvian

Uses Suspense to freeze exiting content during animations; explains Fragment refs, useInsertionEffect, and Suspense DOM-update behavior. A durable web exit-animation technique grounded in React internals.

Rewriting Rive React Native with Nitro Modules: up to 94× Faster Multi-View LoadsMiklós Fazekas (Margelo)

Why the Rive RN SDK's wins came from MODELING, not micro-optimization: the legacy module flattened files/view-models into a per-view God object, so every view re-parsed state; Nitro HybridObjects make them shared typed objects (parse once, render many) — 94× on a 24-view load, ~4.7× lower memory, ~0.3-0.4µs calls vs ~1.5µs TurboModule / ~21µs legacy bridge (iPhone 13 mini, release; the rewrite authors' own benchmark). A general lesson for native-module design: model objects, not modules.

Sources

Depth (in-domain rules) is owned by the react-native-best-practices skill — this entry is selection breadth.

The full explanation

The reviewed long-form essay behind this entry — the why, not a how-to. Also on GitHub.

About animation & gestures — where the per-frame work runs

Diataxis: Explanation. This page builds understanding of the animation landscape — the reasoning behind the picks. It is not an API guide. Animation performance rules (thresholds, profiling) are owned by react-native-best-practices; GPU drawing by RB-E-GAMES/RB-E-SVG. Read this for the why.

The one question that organises everything: which thread pays, every frame?

An animation is a piece of work that must complete every ~16ms, forever, while the rest of your app keeps running. So the durable selection axis is not API style — it is where that per-frame work executes. There are four tiers, each cheaper than the one before:

  1. JS thread (core Animated with useNativeDriver: false) — every frame crosses into your busiest thread. One GC pause, one heavy render, and the animation stutters.
  2. UI thread via worklets (Reanimated) — JS code compiled to run on the UI thread's own runtime (react-native-worklets, now a standalone package). Immune to JS-thread jank, but the worklet itself still costs UI-thread time per frame.
  3. Fully native drivers (native-driven Animated, react-native-ease) — the animation is handed to Core Animation / ObjectAnimator once and no per-frame JS or worklet runs at all.
  4. GPU canvas (Skia) — for drawing that isn't view-property animation at all.

Everything in the entry falls out of this ladder, including the failure modes.

The default, and why

React Native → Reanimated 4 + Gesture Handler 3 for real UI-thread animation and gestures. Web (React DOM) → Motion (framer-motion's successor).

Reanimated 4 sits at tier 2 and is the ecosystem's center of gravity: CSS-style transitions, shared values, and — since RN 0.85 — a shared C++ animation backend with core Animated, so the built-in and the library ride one engine instead of two competing ones. Gesture Handler 3 completes it: gestures are recognized natively and feed worklets directly, and its v3 hook-based API was rebuilt for the React Compiler era (RB-E-REACT-CORE). On the web the same reasoning lands on Motion: declarative layout/exit/gesture animation over compositor-friendly properties — Reanimated and Gesture Handler are React Native-only.

The landscape, tier by tier

Reanimated 4 / react-native-worklets — the tier-2 workhorse. Worklets are now their own package, which matters beyond animation (VisionCamera frame processors, Expo UI synchronous state — RB-E-NATIVE-UI).

Gesture Handler 3 — native gesture recognition; the input half of the animation story.

Core Animated — fine at tier 3 (native driver) for simple cases; since 0.85.1 it can even drive layout props natively. Its trap is tier 1: useNativeDriver: false silently moves the work to the JS thread (the corpus's source-signal rule flags exactly this).

react-native-ease — the tier-3 specialist: animations driven entirely by Core Animation / ObjectAnimator, zero per-frame JS or worklet work. The author's cross-library benchmark shows it holding frame budget at view counts where worklet approaches saturate — read with the stated caveat that the benchmark's author built the winner; the cost model is the durable part.

react-native-screen-transitions — screen/shared-element choreography for React Navigation (RB-E-NAV); featured on the official React Navigation blog, new — pin.

Skia / Lottie / morph-view — tier 4 and specials: react-native-skia for custom GPU drawing (and Lottie via Skottie), lottie-react-native for designer-made animation files, react-native-morph-view for GPU image-morph transitions (niche).

Moti — declarative sugar over Reanimated; dormant since early 2025. Fine if installed; prefer plain Reanimated or LayoutAnimation for new work.

Tradeoffs and failure modes to name out loud

  • useNativeDriver: false — the classic tier-1 trap: JS-thread animation that jitters under load. Native-drive it or move it to Reanimated.
  • Hundreds of concurrently animating views — even tier 2 pays per-frame worklet cost per view; at scale (~hundreds) only tier 3 stays under budget. Benchmark before committing an animation-heavy screen design.
  • Reaching for Skia to animate view properties — GPU canvas is for drawing; transform/ opacity animation belongs in tiers 2–3.
  • Two animation systems fighting — pre-0.85 this was real (Animated vs Reanimated timing); the shared C++ backend resolved it. On ≥0.85 mixing them is no longer an architectural smell.
  • Web habits on native (and vice versa) — Motion doesn't exist on RN; Reanimated doesn't exist on the web. Shared-codebase teams keep animation platform-local (RB-E-CROSSPLATFORM).

How it interacts with the rest of the stack

  • Navigation (RB-E-NAV). Screen transitions are animations with navigation semantics — react-native-screen-transitions builds them on React Navigation rather than forking it.
  • Native (RB-E-NATIVE). Worklets are the same machinery VisionCamera v5 frame processors use; understanding tier 2 here pays off there.
  • Lists (RB-E-LISTS). List re-renders and animations compete for the same frame budget — the "render once" discipline in the STATE/LISTS readings is the other half of smoothness.
  • React core (RB-E-REACT-CORE). Gesture Handler 3's hook API assumes the Compiler era; animation values deliberately live outside React state to avoid re-render coupling.

In one paragraph

Pick animation tooling by which thread pays every frame. React Native's default is Reanimated 4 + Gesture Handler 3 — UI-thread worklets, one shared C++ engine with core Animated since RN 0.85 — with core Animated (native-driven) for trivial cases, ease when hundreds of concurrent animations demand zero per-frame work, Skia for GPU drawing, and react-native-screen-transitions for shared-element navigation choreography. On the web, use Motion. The failure modes are all the same mistake in different clothes: putting per-frame work on a thread that has better things to do.


*See also: RB-E-NAV (screen transitions), RB-E-NATIVE (worklets/JSI machinery), RB-E-GAMES

  • RB-E-SVG (GPU drawing), RB-E-LISTS (the other half of the frame budget). Performance rules and thresholds: the react-native-best-practices skill.*

Related in ui: styling · component-libs · lists · a11y · i18n · charts · editors · svg · maps · calendars · sheets · polish