React Native New Architecture Migration in 2026: What Actually Breaks
Fabric and TurboModules are the default in 2026, but migrating a real app is still messy. Here's what breaks, what to fix first, and where the interop layer saves you.
The New Architecture is no longer experimental. As of React Native 0.76 it's on by default, Expo SDK 52+ ships with it enabled, and the old bridge is scheduled for removal. That doesn't mean flipping the flag is painless. We've moved several production apps across in the last year, and the failure modes are consistent enough to write down.
This is a field guide, not a marketing post. If you're staring at a mid-sized React Native codebase wondering whether the migration is going to eat your sprint, read on.
What the New Architecture actually is
The short version: the old bridge — the asynchronous JSON message bus between JavaScript and native — is gone. In its place you get three things that matter for day-to-day work.
- JSI (JavaScript Interface): a C++ layer that lets JS hold direct references to native objects. Synchronous calls become possible.
- TurboModules: native modules built on JSI, with codegen for type-safe bindings.
- Fabric: the new renderer. Views are created synchronously on the UI thread when needed, and the shadow tree lives in C++.
"Bridgeless mode" is the umbrella term for running with all of these enabled and the legacy bridge fully removed. In 2026, that's the target state.
Why you should care beyond the buzzwords
Two things you'll actually feel:
- List and navigation transitions stop dropping frames on mid-tier Android hardware, because layout no longer round-trips through an async queue.
- Startup gets measurably faster in most apps we've measured — not dramatically, but consistently in the 10–20% range on cold start, in our experience. Your mileage will depend on how many native modules you pull in at boot.
That's the upside. Now the pain.
The libraries that will fight you
Most popular libraries have shipped New Architecture support by now, but "shipped" and "works cleanly in your app" are different claims. The categories of trouble we keep seeing:
Libraries that ship a TurboModule but still register a legacy module. These usually work, but you'll get warnings on boot, and any consumer trying to read module constants synchronously will get surprises. Check the podspec and build.gradle — if you see both codegenConfig and a legacy module registration, expect noise.
View components that haven't moved to the Fabric component API. These are the ones that bite hardest. A legacy RCTViewManager won't render at all under Fabric unless the interop layer is enabled, and even with it enabled, prop updates can lag by a frame or two. Camera libraries, chart libraries, and older map wrappers are the usual suspects.
Anything that touches UIManager.dispatchViewManagerCommand directly. That API still exists but behaves differently. If you were using it for imperative focus/scroll on a legacy view, test every call site.
The interop layer is your friend, temporarily
React Native ships an interop layer that lets legacy view managers and modules keep working under Fabric. Turn it on in your MainApplication or via Expo config:
// Android: MainApplication.kt
override fun getReactHost(): ReactHost =
getDefaultReactHost(
applicationContext,
reactNativeHost,
).apply {
// Enables legacy modules under bridgeless
DefaultNewArchitectureEntryPoint.load(
turboModulesEnabled = true,
fabricEnabled = true,
bridgelessEnabled = true,
)
}
On iOS, the equivalent is setting RCT_NEW_ARCH_ENABLED=1 and letting the default AppDelegate template handle interop registration. Expo users get this via expo-build-properties:
{
"expo": {
"plugins": [
[
"expo-build-properties",
{
"ios": { "newArchEnabled": true },
"android": { "newArchEnabled": true }
}
]
]
}
}
The interop layer is meant as a migration bridge, not a permanent home. Treat any library still relying on it as tech debt with a shot clock.
The migration order that actually works
We've tried both "flip the flag and fix the fires" and "audit everything first." The second is slower to start and much faster to finish. Here's the sequence we use now.
1. Pin your React Native and Expo versions before anything else
Do the migration on a stable version, not on a canary. In 2026 that means React Native 0.76.x or 0.77.x, or the current Expo SDK. Don't chain a version bump and an architecture migration in the same PR unless you enjoy pain.
2. Inventory every native dependency
Run through package.json and mark each native dependency in one of four buckets:
- Native-arch ready: ships codegen specs, works under bridgeless.
- Interop-only: works with the compat layer but hasn't been rewritten.
- Broken: known issues, open bugs, or last release predates 0.74.
- Yours: internal native modules you maintain.
For the broken bucket, decide now: replace, fork, or delay the migration. There's no fourth option.
3. Migrate your own native modules first
If you have internal modules, port them to TurboModules before you touch anything else. The codegen story is straightforward — write a TypeScript spec, run codegen, implement the generated interface — but the first one takes a day to figure out. Do it early while you have patience.
// specs/NativeAnalytics.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
track(event: string, properties: Object): void;
getDeviceId(): Promise<string>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('NativeAnalytics');
4. Enable the New Architecture on a branch, not a flag
Don't ship a runtime flag that toggles between old and new. You'll end up maintaining two codebases. Cut a branch, enable it, fix what breaks, and merge when green.
5. Test on real low-end Android hardware
Fabric's synchronous rendering can expose main-thread work you were getting away with. An app that felt fine on a Pixel 8 can jank on a $150 Android device because a synchronous layout pass is now blocking a frame that used to be deferred. Profile with Perfetto or Android Studio's profiler before you call it done.
The bugs you'll actually hit
A non-exhaustive list from recent migrations:
- Text measurement drift. Fabric measures text slightly differently. Fixed-height containers with tightly wrapped text sometimes clip a descender or wrap one word earlier. Audit any
numberOfLines={1}withellipsizeMode. Animatedvalues that useduseNativeDriver: falseget more expensive under Fabric because the JS-driven path is no longer batched the same way. Move to Reanimated 3 or 4 wherever possible.- Keyboard avoidance behaves differently on iOS. The
KeyboardAvoidingViewin the old renderer relied on layout timing that no longer holds. If you have custom keyboard handling, retest every form. - Modals and portals. Any library that reaches into the view hierarchy to inject a portal — some toast libraries, some bottom sheet libraries — needs its Fabric-compatible version. The old versions often render but don't receive touches.
- Deep linking timing. The JS runtime boots slightly differently under bridgeless. If your deep link handler was racing with initial navigation before, it'll race differently now. Wrap your initial URL handling in a state you can await deterministically.
OTA updates and the New Architecture
One thing that surprises teams: enabling the New Architecture is a native change. You can't ship it via EAS Update or CodePush. A JS bundle built against Fabric assumptions won't run on a native binary still on the old renderer, and vice versa.
Practically: coordinate the switch with a full store release, and gate any OTA updates behind a native version check so you don't push a Fabric-built bundle to an old-renderer binary that some user hasn't updated. EAS Update handles this via runtimeVersion — bump it when you flip the switch.
If you want a refresher on the OTA rules that Apple actually enforces, our earlier writeup on EAS Update and rollback strategy covers the review-safe patterns.
Where we'd start
If you're planning this migration in the next quarter, do these three things this week:
- Run
npx react-native-community/cli doctorandnpx expo-doctoron your current codebase. Fix everything they flag before you touch architecture. - Grep your dependencies for any native library not updated in the last 12 months. Those are your migration blockers, not React Native itself.
- Cut a branch, enable the New Architecture, and boot the app once. The first 30 seconds of red boxes will tell you 80% of what the migration will cost.
The New Architecture is worth the move. The apps we've migrated feel better, especially on Android, and the codegen ergonomics for native modules are a real improvement over the old NativeModules dance. Just don't schedule it as a side quest — it's a two-to-four-week project for a real app, and pretending otherwise is how you end up shipping a broken release.
Want a team like ours?
72Technologies builds production software for the kind of teams who actually read this blog.
Start a projectKeep reading
In-App Purchases in React Native 2026: RevenueCat vs Rolling Your Own
A practical breakdown of when RevenueCat earns its cut and when a direct StoreKit 2 / Google Play Billing integration is the saner call for React Native teams in 2026.
Hermes vs JSC in 2026: What Actually Changed for React Native Startup and Memory
Hermes has been the default in React Native for a while, but the JSC-vs-Hermes decision still shows up in real projects. Here's what's changed, what still bites, and when we'd still reach for JSC.
Deep Linking in React Native 2026: Universal Links, App Links, and the Edge Cases That Break Onboarding
Deep links look trivial until a marketing campaign melts down because iOS opens Safari instead of your app. Here's how we wire universal links, App Links, and Expo Router without the usual footguns.
