Prefer reading? Everything in the video is covered below — plus extra code examples and diagrams.
If you've been building React Native apps for any length of time, you've felt it. The dreaded bridge jank. The mysterious async delays. The moment you realise that no matter how cleverly you optimise your JavaScript, there's a ceiling — and that ceiling was called the bridge.
Well, it's gone. As of React Native 0.83, the legacy architecture has been completely removed from the codebase. Not deprecated. Not disabled. Removed. And in its place is a completely reimagined runtime — faster, more capable, and finally ready to unlock everything that React 18 promised.
In this post, I'm going to break down every piece of the New Architecture, explain why the changes matter, and give you a practical migration path so you know exactly what to do next.
Why the Old Architecture Was Fundamentally Broken
To understand why the new architecture is such a big deal, you need to really understand what the old one was doing under the hood — and why it was always going to hit a wall.
React Native's original design was elegant in concept: run JavaScript on one thread, communicate with native code on another, and use a message-passing bridge to connect them. The problem wasn't the concept. The problem was the implementation.
Every single interaction between JavaScript and the native side required three things: serialize the data to JSON, send it asynchronously, and deserialize on the other end. And then the same process in reverse for the response. This wasn't just slow — it was architecturally limiting in ways that couldn't be fixed by throwing more hardware at the problem.
The three fundamental problems that made the bridge a dead end:
- Everything was async only. No synchronous access to native was possible under any circumstances. Even reading a simple value required a round-trip through the bridge.
- JSON serialization on every crossing. Every piece of data had to be converted to a string, transmitted, and converted back — burning CPU and memory on every single native interaction.
- React 18 was impossible. Concurrent rendering, Suspense, and Transitions require a multi-threaded renderer — something the old UIManager fundamentally couldn't support.
As of React Native 0.83 (2026), the legacy architecture has been completely removed. Setting
newArchEnabled=falsein your gradle.properties does nothing. If you haven't migrated, you need to start now.
The Four Pillars of the New Architecture
The New Architecture isn't one single change — it's a collection of four interlocking systems that together replace everything the bridge used to do. Think of them as four pillars holding up a new foundation.
These four don't work in isolation. JSI is the foundation that makes TurboModules and Fabric possible. Codegen provides the type-safe contract that TurboModules rely on. Pull any one of them out and the system doesn't work.
Deep Dive: JSI — The JavaScript Interface
JSI is the most fundamental change in the entire New Architecture, and it's worth spending real time understanding what it actually does.
In the old system, a native function call added around 5ms of overhead — not enormous on its own, but catastrophic when you're processing 60 frames per second or handling rapid user gestures.
With JSI, the JavaScript runtime holds direct C++ memory references to native objects. When JS calls a native function, it's calling a function pointer directly — no JSON, no queue, no async wait. The overhead drops from ~5ms to ~0.1ms. That's a 40x improvement.
A Real-World Example: VisionCamera
The best illustration of what JSI makes possible is VisionCamera 4.0. Processing a single 4K video frame produces around 30MB of raw pixel data. At 60 frames per second, you're moving roughly 1.8GB of data per second between JS and native. With the old bridge and JSON serialization, this was not just slow — it was impossible. With JSI, VisionCamera passes direct memory references to frame data without copying a single byte.
// This runs synchronously on every video frame via JSI
const frameProcessor = useFrameProcessor((frame) => {
'worklet'
// Direct memory access — no serialization, no bridge crossing
const result = detectObjects(frame) // runs on native thread
runOnJS(setDetections)(result)
}, [])
// OLD WAY: would have serialized 30MB to JSON per frame — impossible at 60fps
// NEW WAY: passes a memory reference — zero copy cost
Key insight: JSI doesn't mean everything becomes synchronous. Async is still the right choice for network calls, disk I/O, and anything that shouldn't block the JS thread. JSI just allows synchronous access when you need it — that optionality is what was missing before.
Deep Dive: Fabric — The New Renderer
While JSI handles how JavaScript communicates with native, Fabric handles how React Native renders your UI. The previous renderer, UIManager, maintained the shadow tree — the internal representation of your component layout — in JavaScript, which meant every layout calculation had to flow through the same async bottleneck. Fabric moves the shadow tree to C++, enabling it to be shared across multiple threads simultaneously.
With Fabric, React Native can now interrupt and resume renders based on priority, batch multiple state updates automatically, use startTransition to mark non-urgent updates, and support Suspense for data fetching with native loading states.
import { startTransition, Suspense } from 'react'
// Mark a state update as non-urgent — Fabric can deprioritise it
const handleSearch = (text) => {
startTransition(() => {
setSearchResults(text) // won't block user input
})
}
// Suspense for data — impossible with old UIManager
function ProfileScreen() {
return (
<Suspense fallback={<LoadingSkeleton />}>
<UserProfile />
</Suspense>
)
}
Deep Dive: TurboModules & Codegen
TurboModules — The End of Eager Loading
In the old system, when your React Native app launched, it loaded every single native module you had registered — camera, Bluetooth, payments, push notifications — all of them, before your first screen rendered. For a typical production app with 20-30 native modules, this was a significant chunk of your cold start time, gone before you'd even rendered a single component.
TurboModules are lazy. They don't load until the first time your code actually calls them. If a user never opens your camera screen, the camera module never initialises.
// Define your module spec in TypeScript
import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport'
import { TurboModuleRegistry } from 'react-native'
export interface Spec extends TurboModule {
multiply(a: number, b: number): number
getDeviceName(): string
}
// Codegen reads this spec and auto-generates native bindings
// The module ONLY loads when this line executes for the first time
export default TurboModuleRegistry.getEnforcing<Spec>('MyModule')
Codegen — Type Safety You Don't Have to Write
Codegen eliminates runtime type mismatches by generating native bindings directly from your TypeScript interfaces at compile time. If the types don't match, the build fails. Not the app — the build.
// android/app/build.gradle
react {
jsRootDir = file("../src")
libraryName = "MyAppModules"
codegenJavaPackageName = "com.myapp.modules"
}
// Codegen auto-generates Java/Kotlin + ObjC/Swift bindings
// from your TypeScript specs at build time
The Performance Numbers
These numbers come from production migrations at Shopify and Meta — real apps, real users, real conditions.
The cold start improvement is largely from TurboModules — deferring 20-30 modules from startup to on-demand has a massive cumulative effect. The rendering improvement comes from Fabric's C++ shadow tree removing the async round-trip from layout calculations. The memory improvement comes from a combination of TurboModules and Hermes's improved garbage collection.
Your mileage will vary. Apps with fewer native modules will see smaller cold start gains. Apps with heavy list rendering and animations will see larger rendering improvements. Always benchmark your specific app before and after migration.
Migration Timeline — How We Got Here
How to Migrate Your App (Step by Step)
Most migrations for apps without custom native modules take a few days to a week. Apps with significant custom native code can take two to eight weeks.
-
Audit your dependencies first.
Use
npx expo-doctorfor Expo projects, or check the React Native Directory for every library in your project. This tells you how hard your migration will be before you commit. - Upgrade React Native. Target RN 0.76 or higher with Expo SDK 52 or higher. Use the RN Upgrade Helper — it shows you the exact diff of changes needed between versions.
-
Enable the New Architecture.
Add
newArchEnabled=truetoandroid/gradle.propertiesandRCT_NEW_ARCH_ENABLED=1to your iOS Podfile. Then do a full clean build — not a cached one. - Fix incompatible libraries. The Interop Layer handles many cases automatically. For libraries that still break, find a maintained fork, submit a PR, or find a replacement. Most popular libraries are already compatible.
- Test on real devices, especially Android. Low-end Android devices are where improvements are most dramatic — and where edge cases surface. Use React Native Inspector 2.0 to profile frame rates.
# android/gradle.properties
newArchEnabled=true
hermesEnabled=true
# ios/Podfile — add before your target block
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
platform :ios, '13.4'
prepare_react_native_project!
target 'YourApp' do
config = use_native_modules!
use_react_native!(
:path => config[:reactNativePath],
:hermes_enabled => true
)
end
Library Ecosystem Status in 2026
As of May 2026, the library ecosystem is in very good shape — especially for the libraries most apps actually depend on.
- React Navigation 7.2+ — Full support. Native Stack fully optimised for Fabric.
- Reanimated 3.5.1+ — Full support. Worklets run via JSI — actually faster than before.
- Gesture Handler 2.16+ — Full support.
- Expo SDK 52+ — Full support. All Expo modules New Arch compatible.
- VisionCamera 4.0+ — Full support. Built specifically around JSI frame processors.
- MMKV 2.12+ — Full support. JSI-based from day one.
- react-native-maps 1.14+ — Partial. Works via Interop Layer, full migration ongoing.
- react-native-camera (legacy) — Unmaintained. Migrate to VisionCamera 4.0 instead.
83% of Expo SDK 54 projects built with EAS Build in January 2026 were already running on the New Architecture. If a library you depend on still doesn't support it, that's a signal about whether it's actively maintained.
What's Coming Next
React Native 0.84 — Hermes V1 Bytecode Caching
On subsequent app launches, Hermes reads pre-compiled bytecode instead of parsing and compiling your JavaScript bundle from scratch. Another cold start improvement on top of what TurboModules already delivered.
React Server Components in Expo Router
Certain components run on a server, stream their output to the mobile client, and the device never receives the JavaScript for those components at all. Smaller bundles, faster startup, server-side data access without building a separate API layer.
The React Compiler
Auto-memoizes your components and hooks at compile time. Large portions of your useMemo and useCallback calls become unnecessary — the compiler figures out what to memoize and does it for you.
Spatial Computing
visionOS and AR are being positioned as first-class React Native targets. Fabric's C++ rendering model is what makes supporting these platforms feasible without maintaining entirely separate codebases.
Your Action Checklist
If you've read this far, here's what to actually do today:
- Open
package.jsonand check your current React Native version. - If you're below 0.76, start planning your migration sprint.
- Run
npx expo-doctoror check React Native Directory for every native library. - Create a migration branch — don't do this on main.
- Measure your cold start time on a real device. You need a baseline.
- Enable
newArchEnabled=trueon Android andRCT_NEW_ARCH_ENABLED=1on iOS. - Fix any libraries that break. Most won't need it.
- Measure cold start again and compare the numbers.
- Ship it. The migration path is well-trodden at this point.
The New Architecture isn't a future upgrade — it's the present. Every week you stay on the old architecture is a week of performance you're leaving on the table. The migration path is clear, the ecosystem is ready, and the gains are real.