Technology · Mobile Development
React Native's New Architecture in 2026: What the Fabric and JSI Migration Actually Took
React Native's new architecture shipped as stable and has been default since RN 0.76. Here's what the Fabric renderer and JSI bridge replacement changed, what the migration looks like in practice, and where the remaining rough edges are.
Anurag Verma
8 min read
Sponsored
React Native had a reputation problem that was hard to argue with. The bridge (the async, JSON-serialized communication layer between JavaScript and native code) was a genuine bottleneck. Every call from JS to a native module went through serialization, crossed the bridge asynchronously, got deserialized on the other side, and returned the same way. For common operations like scroll events, animations, or continuous gestures, this created visible jank that developers spent years working around with tools like react-native-reanimated running directly on the UI thread.
The new architecture (Fabric renderer plus JSI plus TurboModules) addressed this at the runtime level. It’s been in development since 2019, became opt-in stable in RN 0.74, and is the default in 0.76 and beyond. If you started a new project any time in the past year, you’re already on it. If you’re maintaining an older app, you need a migration plan.
Here’s what actually changed, what the migration took, and where the remaining friction is.
What the Old Bridge Was Doing Wrong
The old architecture had a single JS thread, a native thread, and a shadow thread for layout. Communication between them went through the asynchronous bridge. Every setState triggered a render, which triggered a shadow tree diff in JavaScript, which serialized a description of changes across the bridge, which got applied to the native view hierarchy.
This caused specific, reproducible problems:
Gesture lag. If a user scrolled fast and the JS thread was busy processing other work, scroll events would queue up behind the existing work. The native side knew the user was scrolling but couldn’t get updated position data from JS quickly enough. You’d feel the delay.
Synchronous native calls were impossible. Calling a native module returned a Promise. There was no way to call native code and get a result back in the same JS execution frame. This blocked patterns like reading a layout value and immediately making a decision based on it.
Animation stutter. Any animation that needed JS to update a value on each frame would stutter whenever the JS thread was busy. This happened during hydration, during heavy state updates, and on lower-end Android devices where JS execution was simply slower.
What JSI Changed
JSI replaces the bridge with a direct JavaScript-to-C++ host object interface. Instead of serializing arguments and posting them to a queue, JavaScript can call native C++ functions directly. The objects are shared across the JS and native runtimes, with no serialization and no queuing.
The practical result:
- Synchronous native calls are now possible. If you need to read a native value and use it immediately, you can.
- Native code can hold a reference to a JS object and call back into it later without going through the bridge event system.
- Reanimated and Gesture Handler can share a common worklet runtime with the native UI thread, enabling 60/120fps animations that are immune to JS thread congestion.
JSI itself is just the interface layer. TurboModules are the actual native modules rebuilt to use JSI instead of the bridge. Fabric is the new rendering pipeline that uses JSI to synchronize the React fiber tree with native views.
What Fabric Changed
Fabric replaced the UIManager bridge pattern with a three-layer rendering system: React fiber tree (JS), Shadow tree (shared C++ layer), and Host view tree (native platform views).
The Shadow tree now lives in C++, not in JavaScript. This means layout calculation (via Yoga) happens in the shared C++ layer rather than requiring JS→bridge→native round trips. The commit phase (where React confirms a render is complete and the native side should update) can now happen synchronously or concurrently.
This is what makes Concurrent React work on React Native for the first time. React 18’s concurrent features, startTransition, Suspense, and concurrent rendering, require the ability to interrupt and resume rendering. That wasn’t possible with the old bridge. With Fabric’s synchronous commit capability, they work correctly.
import { startTransition, useState } from 'react'
import { FlatList, TextInput } from 'react-native'
function SearchScreen({ items }) {
const [query, setQuery] = useState('')
const [filtered, setFiltered] = useState(items)
const handleChange = (text) => {
setQuery(text)
// Mark the list update as non-urgent
// The input stays responsive even on large lists
startTransition(() => {
setFiltered(items.filter(i => i.name.includes(text)))
})
}
return (
<>
<TextInput value={query} onChangeText={handleChange} />
<FlatList data={filtered} renderItem={({ item }) => <ItemRow item={item} />} />
</>
)
}
On the old architecture, this search pattern would drop input frames on large lists. On the new architecture with startTransition, the input update is committed synchronously while the list re-render yields.
The Migration Path for Existing Apps
If you’re migrating an existing app to the new architecture, the process depends on how many native modules you have and whether they’re community packages or custom code.
Step 1: Audit your native modules. Run npx react-native info and look at your installed packages. Any package that uses native code needs to be compatible with the new architecture (either interop-layer compatible or TurboModule-ready). The React Native Directory (reactnative.directory) shows architecture compatibility for community packages.
Step 2: Enable the interop layer. Since RN 0.74, an interop layer allows old-architecture native modules to work with the new rendering pipeline without requiring the library authors to rewrite everything. Enable it in your project:
// metro.config.js
const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')
const config = {
resolver: {
unstable_enablePackageExports: true,
},
}
module.exports = mergeConfig(getDefaultConfig(__dirname), config)
// AppDelegate.mm — enable new arch
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.moduleName = @"YourApp";
self.initialProps = @{};
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
The interop layer buys you time to migrate dependencies without blocking the new architecture adoption.
Step 3: Update or replace incompatible packages. If a package doesn’t work with the interop layer (some don’t), your options are: find an alternative package, fork and update it, or rewrite the affected functionality using Expo Modules API, which creates new-architecture-ready native modules with much less boilerplate.
Step 4: Enable new architecture in Gradle and Podfile.
For Android (android/gradle.properties):
newArchEnabled=true
For iOS (ios/Podfile):
# This is now the default since RN 0.76, but for older projects:
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
Run pod install after updating the Podfile.
What the Migration Actually Cost Teams
The teams that had the smoothest migrations were running relatively modern dependency stacks. Apps that had avoided native module sprawl and were using well-maintained packages from the React Native ecosystem (React Navigation, React Native MMKV, React Native Reanimated, Expo modules) found that most of their dependencies were already new-architecture compatible.
Apps with custom Objective-C or Java native modules required more work. The modules needed to be rewritten to conform to the TurboModules spec or wrapped in compatibility shims. A rough estimate: one custom native module takes 1-2 days to migrate, assuming a developer who understands the platform.
The most common blockers in order:
- Legacy native modules that the library author hadn’t updated and had no interop shim
- Custom native views that needed to be rewritten as Fabric components
- Assumptions in custom code about the bridge event system that didn’t hold under JSI
Where the Rough Edges Remain
The new architecture is better. Performance improvements on scroll-heavy and animation-heavy screens are visible on real devices. But it’s not finished work.
Codegen warnings. The new architecture uses Codegen to generate type-safe native bindings from your TypeScript definitions. If your prop types aren’t exactly what Codegen expects, you’ll get warnings or silent failures. The error messages are better than they were but still often don’t point directly at the problem.
Debug experience on Android. The debugger for the Hermes engine (used on the new architecture) is solid on iOS. On Android, the Metro bundler and DevTools connection is reliable, but the native-side debugging story for TurboModules is still rougher than you’d want.
Third-party package ecosystem. The interop layer saved most packages from needing immediate rewrites, but interop has limits. Some edge cases, particularly around view commands and event emitters, don’t go through the interop layer correctly.
New Projects: Just Use Expo
For any new React Native project in 2026, start with Expo. The managed workflow handles the new architecture by default, Expo Modules provides a clean TypeScript API for writing new-architecture native code when you need it, and the ecosystem integrations are built and tested against the current architecture.
The “bare React Native vs Expo” debate has moved substantially toward Expo for most use cases. You can eject to bare workflow if you have requirements the managed workflow can’t meet. But starting bare to “have more control” means taking on maintenance work before you know you need that control.
The new architecture is a real improvement. For animation performance, gesture handling, and concurrent feature support, it delivers what it promised. The migration cost was real but bounded. Most production apps in the React Native ecosystem are now through it.
Sponsored
More from this category
More from Technology
Sponsored
The dispatch
Working notes from
the studio.
A short letter twice a month — what we shipped, what broke, and the AI tools earning their keep.
Discussion
Join the conversation.
Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.
Sponsored