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
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
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
option
tradeoff
evidence
Reanimated 4
UI-thread + CSS-style animations/transitions; worklets split into react-native-worklets; requires New Arch
6.3M/wk · ships in 12/34
Gesture Handler 3.0
native gestures; v3 adds a hook-based API built for the React Compiler + Reanimated 4
6.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 note
5.6M/wk · ships in 10/34
react-native-skia (2.x)
GPU 2D drawing; v2.1 plays Lottie via Skottie; pairs with Reanimated
1.1M/wk · ships in 3/34
lottie-react-native
designer Lottie/JSON animations; v7 adds visionOS
1.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-04
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 / LayoutAnimation
declarative 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 path
379k/wk
react-native-screen-transitions
custom 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, newer
93k/wk
react-native-ease
animations 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 reading
21k/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 instead
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.
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.
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.
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 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.
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).
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.
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.
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:
JS thread (core Animated with useNativeDriver: false) — every frame crosses into your
busiest thread. One GC pause, one heavy render, and the animation stutters.
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.
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.
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.