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

entriesplatform-native · verified 2026-07-10 · react-native

Native modules & the New Architecture

reviewedconfidence: hightrack: weakenedthis tier holds 75% on the public scorecard (6/11 graded · 0 overturned) →

cited by: media

re-verified 4× — 2026-07-13 · 2026-07-10 · 2026-07-09 · 2026-07-06 · changelog

recommendation

Target the New Architecture (mandatory ≥ RN 0.82). Reach for Expo Modules for most native needs; use Nitro Modules for performance-critical custom native.

  • most native integrations → Expo Modules (managed, fast path)
  • perf-critical custom native (camera, audio, BLE) → Nitro Modules — but it's pre-1.0 (v0.36.x, 2026-07), third-party (not in RN core), and still ships breaking changes, so pin versions; for non-extreme needs, plain Turbo Modules are zero-dep, in-core, and stable
  • standard custom native → Turbo Modules / Fabric
  • still on Legacy Arch → migrate; it's frozen and being stripped from core

Options & tradeoffs

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

optiontradeoffevidence
Expo modulesmanaged, fast path for many needs5.7M/wk · ships in 2/14
Turbo Modules / Fabric (JSI)custom native; New Architecture
Nitro Modulescodegen-driven JSI; positioned as Turbo Modules' successor; vendor claims ~10x vs Turbo, ~100x vs Expo Modules1.1M/wk · ships in 7/14

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-nitro-modules 1.1M · expo-modules-core 5.7M · expo 6.8M

Verified notes

ARCHITECTURE: the New Architecture (Fabric + Turbo Modules over JSI, no async bridge) was the default for new projects from RN 0.76, became the ONLY runtime option in RN 0.82, and RN 0.84 keeps stripping Legacy-Arch code from iOS + Android — all new native work targets it; Legacy is effectively gone. Nitro Modules is consolidating the custom-native ecosystem (mmkv v4, device-info, image, video, fetch, healthkit, geolocation moved to it; VisionCamera v5 rewrote ~3,000 lines of JSI/C++ onto Nitro). EDGE-TO-EDGE: RN 0.86 (2026-06-11) moved Android-15+ edge-to-edge into core, fixing KeyboardAvoidingView / Dimensions / StatusBar — projects on RN >= 0.86 can DROP the standalone `react-native-edge-to-edge` (zoontek); on RN < 0.86 it is still required. Arch facts verified vs the RN 0.84 blog; edge-to-edge vs the RN 0.86 blog. The bridge interop layer was fully removed in RN 0.85. Adversarially stress-tested 2026-06-25 (SURVIVES; Nitro stability caveat added — Nitro is pre-1.0/third-party, Turbo Modules stay the stable in-core option). EXPO SDK 56 (verified vs its changelog, 2026-07-09): expo-modules-core dropped the Objective-C++ intermediary for DIRECT Swift↔C++ JSI interop — "fewer hops means less call overhead", significant native-module-call speedup, and Expo module authoring is "Swift all the way down". SDK 56 also lets you write inline Expo Modules (Swift/Kotlin in-app with generated TypeScript). Expo Modules also ships SHARED OBJECTS/REFS (verified vs docs.expo.dev/modules/shared-objects) — JS handles to live native objects instead of serialized copies; the next-gen MediaLibrary/Contacts APIs are built on it.

Canonical reading

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

How to Make Pure JSI Code Faster in React Native (Part 1)Alex Shumihin (Margelo)

How JSI bindings actually work at the C++/Hermes boundary — HostFunction vs HostObject, the Object+NativeState pattern Nitro compiles down to, stack vs heap, minimizing JS-to-native crossings — with reproducible Hermes benchmarks. The runtime mechanics under Turbo/Nitro Modules.

Making JSI Faster with More Efficient Data Structures (Part 2)Alex Shumihin (Margelo)

How data representation across the JSI boundary (array-of-objects vs flat array vs ArrayBuffer, numeric vs string contracts) can swing performance up to ~30x with no algorithmic change. First-principles guidance for New-Architecture native modules.

How Margelo Helped Discord Improve React Native's New Architecture PerformanceHanno Gödecke (Margelo)

Low-level New-Architecture jank postmortem: Reanimated's cloneShadowTreeWithNewProps over-cloning the Fabric shadow tree plus redundant layout passes for non-layout props, with fixes upstreamed. Canonical Fabric / New-Arch internals.

Four Years of React Native Quick Crypto: From Wallets to Node ParityBrad Anderson (Margelo)

A four-year native-library architecture case study: wrapping OpenSSL for Node behavior-parity, a sync-first threading model with async escape hatches, the hand-written-JSI to Nitro codegen migration, and security audit as architecture validation. Generalizable lessons for native-module authors.

Watching

Curated talks, podcasts & videos — the A/V companion to the reading list.

RNR 351 — Transforming Packages to Nitro with Marc RousavyReact Native Radio (guest: Marc Rousavy, Nitro/VisionCamera author)

37-min episode (2026-01) from Nitro's author on why Nitro exists (JSI boilerplate), how it compares to Turbo Modules and Expo Modules, and the ecosystem's package-migration wave — the audio companion to this entry's Nitro-consolidation note.

Sources

Depth (in-domain rules) is owned by the react-native-jsi 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 native modules & the New Architecture (React Native)

Diataxis: Explanation. This page builds understanding of how RN talks to native code and which module path to choose. It is not a tutorial: the candidate list lives in the index entry RB-E-NATIVE; JSI memory-ownership and threading rules are owned by the react-native-jsi skill. Read this for the why; read that skill to do it safely.

The shift that changed everything: the bridge is gone

For years React Native's JS and native sides talked over an asynchronous bridge that serialised every call to JSON. That bridge was the source of most "RN is slow at the boundary" pain: batched, async, copy-heavy. The New Architecture removes it. JS and native now share a C++ layer — JSI (the JavaScript Interface) — and call each other synchronously, without serialization. On top of JSI sit Fabric (the renderer, with an immutable C++ shadow tree) and Turbo Modules (native modules over JSI). This is the single fact that organises every native decision: there is no async bridge anymore; native is a synchronous, C++-mediated call away.

Two consequences: native calls can be synchronous and cheap, and a whole class of native libraries can be rebuilt on JSI for large speedups. The cost is that the boundary is now real C++ — memory ownership and threading matter (which is why depth defers to react-native-jsi).

This is not optional anymore

The New Architecture is the architecture. It became the default for new projects in RN 0.76, the only runtime option in RN 0.82 (opt-out ignored), and RN 0.84 began stripping Legacy-Architecture code from iOS and Android. Legacy was frozen at 0.80 and is effectively gone. So "should we migrate to the New Architecture?" is no longer a question — all new native work targets it, and apps still on Legacy are on borrowed time (RB-E-RN-VERSIONS). (Verified against the official RN 0.84 and 0.86 release blogs.)

The default, and why

Target the New Architecture (mandatory ≥ RN 0.82). Reach for Expo Modules for most native needs; use Nitro Modules for performance-critical custom native.

The selection is about how much native you're writing and how hot the path is. Expo Modules is the managed fast-path: a clean Swift/Kotlin authoring model that covers the vast majority of integrations with the least ceremony — the right default even in bare RN. When you need a custom native module on a hot path (camera frames, audio, BLE, crypto), Nitro Modules — codegen-driven JSI, positioned as Turbo Modules' successor — is the performance choice and is consolidating the ecosystem (mmkv v4, device-info, image, video, fetch, and VisionCamera v5 all moved onto it). Turbo Modules / Fabric directly is the standard path when you're writing conventional custom native and don't need Nitro's edge.

The landscape, and when each one wins

Expo Modules — managed, ergonomic, broad coverage; the default for most native needs. You write idiomatic Swift/Kotlin and get a clean JS surface. Not Expo-app-only — usable in bare RN.

Turbo Modules / Fabric (JSI) — the New Architecture's own module and rendering system. The baseline for custom native work; you're writing against codegen and JSI directly.

Nitro Modules — codegen JSI with an emphasis on raw performance and ergonomics; the ecosystem's consolidation point for perf-critical native. Reach for it when the boundary is the bottleneck — high-frequency calls, large buffers (ArrayBuffers to avoid serialization), real-time pipelines. The durable mechanics of why JSI code is fast (HostObject vs HostFunction, data-shape choices) are worth understanding — see the reading.

Tradeoffs and failure modes to name out loud

  • Hand-writing JSI you didn't need to. Most "I need native" cases are an Expo Module, not raw JSI. Drop to Turbo/Nitro/JSI only when a managed module genuinely can't do it or the path is hot.
  • Ignoring the C++ ownership model. With the bridge gone, the boundary is real memory and real threads; lifetime bugs and cross-runtime object passing are the new failure class. This is exactly what react-native-jsi owns — route there before shipping custom native.
  • Treating "New Arch migration" as optional. It isn't (mandatory 0.82, stripped 0.84). Budgeting it as a someday-task on a pre-0.82 app is a planning error (RB-E-RN-VERSIONS).
  • Serializing across a non-serialized boundary. A common Nitro/JSI lesson: passing big arrays-of-objects or strings where an ArrayBuffer/typed contract would do can swamp the gains. Data shape across the boundary is a first-order performance decision.

How it interacts with the rest of the stack

  • RN versions (RB-E-RN-VERSIONS). The New Architecture's mandatory/stripped timeline is the reason to track RN versions; this entry explains the architecture those versions ship.
  • Media (RB-E-MEDIA) & storage (RB-E-STORAGE). The flagship Nitro/JSI consumers: VisionCamera v5 (frame processors), MMKV v4, op-sqlite. Those entries are the domain; this one is the module system underneath.
  • Build (RB-E-BUILD). Codegen, precompiled iOS binaries, and Hermes V1 are the build-time side of the New Architecture.
  • JSI depth (react-native-jsi). Memory ownership, threading, and JSI API correctness live there — this page tells you which module path, that skill tells you how to not crash.

In one paragraph

The New Architecture deleted the async bridge: JS and native now share a synchronous C++ layer (JSI), with Fabric rendering and Turbo/Nitro Modules for native code. It's mandatory from RN 0.82 and Legacy is being stripped, so all new native work targets it. Choose by how much native you're writing: Expo Modules for most needs (managed, even in bare RN), Nitro Modules for performance-critical custom native (it's consolidating the ecosystem), Turbo Modules/Fabric for standard custom work — and push JSI memory/threading correctness down to react-native-jsi, because the boundary is now real C++.


See also: RB-E-RN-VERSIONS (the mandatory-migration timeline), RB-E-MEDIA / RB-E-STORAGE (flagship Nitro/JSI consumers), RB-E-BUILD (codegen, Hermes V1, precompiled iOS). JSI memory/threading depth: the react-native-jsi skill.

Related in platform-native: media · storage · native-ui · keyboard · payments · brownfield · games · alt-frameworks