Experiment With the New Architecture of React Native

Authors
Oskar Kwaśniewski
Software Developer
@
Callstack
Issue: Your app is using old architecture without the concurrent features of React 18

Maybe it's better to say “current” architecture since it's still mostly used by production apps. This term refers to how React Native's two realms (Native and JS) communicate with each other. Both new and old architecture is based on the communication between JavaScript and the native side. Currently, this communication is
handled by the bridge. Let's go over its limitations to easier understand the problems that the New Architecture is trying to solve.

  • It is asynchronous: the JavaScript side submits data to a bridge and waits for the data to be processed by the native side.
  • It's single-threaded (that's why it's important to not overload the JS thread and execute animations on the UI thread). 
  • It adds additional overhead when it comes to the serialization of data from JSON objects.

The bridge is still working fine for most use cases. However, when we start to send a lot of data over the bridge, it may become a bottleneck for our app. This problem can be seen when rendering a lot of components in a long list. In the case when the user scrolls fast, there will be a blank space caused by the communication between the JS and native sides being asynchronous. Essentially what happens is that we are having a “traffic jam” on our bridge with objects waiting to be serialized. The same issue with the bridge being “overloaded” can be seen in native modules sending a lot of data back and forth.

This bottleneck, together with providing a type-safe way of communicating between native and JS, are the main things that the New Architecture is trying to solve. However, not everything about the New Architecture is as good as it may seem. We will also get into the drawbacks that it brings.

Migrate your app to the New Architecture

What is the new React Native architecture?

Starting from React Native 0.68, developers can leverage new capabilities of the framework. The New Architecture relies on a series of tools, which are key components to the new experience. Two most important ones are Fabric and TurboModules. The first one is a new rendering system, and the second one is a new way of writing native modules. We will get into details later in this section.

Codegen and JSI are two new tools improving the developer experience. They are essential to understand how the New Architecture works. Codegen drastically improves DX by generating a lot of native boilerplate code and ensuring type safety. And JSI, a C++ API for interacting with any JS engine.

Note: New Architecture is still considered experimental. Always use the latest version of React Native when using it.

Codegen

A code generation tool that makes JS source of truth by automating the compatibility between JS and the native side. It allows towrite statically typed JS (called JS Spec) which is then used to generate the interface files needed by Fabric native components and TurboModules. Spec consists of a set of types written inTypeScript or Flow that defines all the APIs provided by the native module

Codegen ensures type-safety and compile-time type safety, which means smaller code and faster execution as both realms can trust each other around validating the data every time. To find out more about it, refer to the docs.

JSI

JSI is the foundation of the New Architecture, a C++ API for interacting with any JS engine. In contrast to the bridge which was asynchronous, JSI is synchronous which allows for invoking native functions faster. It lets JavaScript hold references to C++host objects and invoke methods directly on them. This removes the major overhead of asynchronous communication between JS and native by serializing objects using the bridge.

Fabric

Fabric is React Native's new concurrent rendering system, a conceptual evolution of the legacy render system. The core principle is to unify more render logic in C++ to better leverage interoperability between platforms. Host Components like View, Text, etc. are now lazily initialized, resulting in faster startups. Fabric allows us to take advantage of the features introduced in React 18.

TurboModules

This is a new way of writing native modules that also leverages the power of JSI, allowing for synchronous, and an order of magnitude faster data transfer from native to JS and vice versa.

It is a rewrite of the communication layer between JavaScript and platform native modules like Bluetooth, Biometrics, etc. It also allows for writing native code for both platforms using C++ and introduces the lazy loading of modules to speed up your app startup time.

Bridgeless mode

The ultimate goal of the New Architecture is to fully sunset the bridge. Starting from React Native 0.73, you can enable Bridgeless Mode which will disable the creation of the bridge entirely.

This will result in a slightly faster app startup due to removing the overhead of loading the rest of the React Native runtime: error handling, global event emitters, timers, and more.

How to turn on New Architecture

According to the official React Native core team recommendation, in order to turn on the New Architecture in your app, you need to update your app to the latest version of React Native.

To migrate your app to the New Architecture, follow these steps:

  1. Upgrade your app to the latest React Native version, you can use the upgrade helper.
  2. [Android] Set newArchEnabled=true in gradle.properties.
  3. [iOS] Run RCT_NEW_ARCH_ENABLED=1 pod install inside the iOS folder.
  4. Run the app in debug and release modes. Look for Components that are not yet compatible – they will show as red boxes – Unimplemented component: ComponentName – and you will likely notice them.
  5. In case of unsupported components, use the Interop Layer through the react-native.config.js file and the unstable_reactLegacyComponentNames option and try again. Take note that the interop layer is not fully compatible with the old rendering and event system, so inconsistencies may be expected in some cases.

Benefits of moving React Native app to New Architecture

Now that you know the basics of how the New Architecture works, let’s go over the benefits.

Performance

Due to the synchronous nature of the new architecture, while communicating with the native side, there will be some performance improvements. The app's startup time will be significantly reduced as every native module will be lazily-loaded. Once the bridgelessmode will be available it will also remove the overhead of loading the bridge at startup. However, not every scenario proves this, in some of the benchmarks architecture performance is worse.

Meta's goal was not to make new architecture X times faster thanthe old one. Apart from removing major bottlenecks they wantedto create a new solid foundation which would allow for new capabilities that could not be developed using previous architecture. Migration of the Facebook app took over a year and they haven't noticed any significant performance improvements or regressions that are perceivable by the end user. However, this doesn't mean that performance improvements won't come in the future. Now that they reworked internals they have a great foundation to build upon.

Let’s go over some performance benchmarks by Alexandre Moureaux from BAM. Here is a link to the source.

Benchmark of rendering 10K views

In this case, the New Architecture proves more efficient than the old one. Using, on average, less CPU but more RAM.

chart showing CPU usage per thread

Benchmark of rendering 2K Text components

In this scenario, the old architecture is faster, mainly because of heavier UI thread consumption.

chart showing rendering 2k text components

The official response from the React Native team is that their internal benchmarks while rolling out the New Architecture to users were neutral across all React Native surfaces in the Facebook app on both Android and iOS. As stated by Samuel Susla in this discussion thread, “In the last years, we conducted dozens of tests in production on millions of devices to assure performance was neutral.”

So in most use cases, you can expect a neutral performance impact without any performance regressions. And keep in mind that the New Architecture is getting better every single day with many developers contributing to the repository, so the results may be totally different by the time you are reading this.

Future readiness

The React Native New Architecture allows your app to leverage Concurrent React features. This improves UI responsiveness, provides Suspense for data fetching to handle complex UI loading schemes, and ensures your app is ready for any further React innovations built on top of its new concurrent engine introduced in React 18.

Let’s see how we can leverage React18’s startTransition API to prioritize between two state updates. In our example, a button click can be considered an urgent update, whereas the NonUrgentUI can be considered a non-urgent update. To tell React about a non-urgent update, we can wrap the setState in the startTransition API. This allows React to prepare a new UI and show the old UI until a new one is prepared. 

In our example, we wrapped setNonUrgentValue in start-Transition and told React that nonUrgentValue is a transition and not so urgent, it may take some time. We’ve also added a conditional backgroundColor. When you run this example, you will see that once you click on the button, the view will retain its old UI, e.g., if we start at value 1, the UI will be green.

Once you click on the button, the Value text UI will be updated, but the UI for the container will remain green until the transition is completed, and the color will change to red due to the new UI being rendered. That’s the magic of React’s concurrent rendering.

To understand it better, assume that wrapping an update in startTransition renders it in a different universe. We don’t see that universe directly, but we can get a signal from it using the isPending variable returned from the useTransition hook. Once the new UI is ready, both universes merge together to show the final UI.

import React from "react";
import { Button, StyleSheet, Text, View } from "react-native";
const dummyData = Array(10000).fill(1);
const NonUrgentUI = ({ value, isPending }) => {
const backgroundStyle = {
backgroundColor: value % 2 === 0 ? "red" : "green",
};
return (
<View>
<Text>Non urgent update value: {isPending ? "PENDING" : value}</Text>
<View style={[styles.container, backgroundStyle]}>
{dummyData.map((_, index) => (
<View key={index} style={styles.item} />
))}
</View>
</View>
);
};
const ConcurrentStartTransition = () => {
const [value, setValue] = React.useState(1);
const [nonUrgentValue, setNonUrgentValue] = React.useState(1);
const [isPending, startTransition] = React.useTransition();
const handleClick = () => {
const newValue = value + 1;
setValue(newValue);
startTransition(() => {
setNonUrgentValue(newValue);
});
};
return (
<View>
<Button onPress={handleClick} title="Increment value" />
<Text>Value: {value}</Text>
<NonUrgentUI value={nonUrgentValue} isPending={isPending} />
</View>
);
};
export default ConcurrentStartTransition;
const styles = StyleSheet.create({
container: {
flexDirection: "row",
flexWrap: "wrap",
},
item: {
width: 10,
height: 10,
},
});

To understand it better, let’s visualize the code snippet we just went through. The image below shows a comparison of when we use startTransition and when we don’t. Looking at the image, we see that React flushes the urgent update right off, which happens due to calling setValue without wrapping it in startTransition. Next, we see that React shows the old UI (viewed in green) for the UI that depends on the non-urgent updates, which means the updates that are wrapped in startTransition.

We also see a Pending text displayed; this is a way for React18 to tell us that the new UI depending on this state is not yet ready. Once it’s ready, React flushes it and we don’t see the Pending text anymore, and the view color changes to red. On the other hand, if we don’t use startTransition, React tries to handle both updates as urgent and flushes when both are ready. This certainly has a few downsides, such as the app trying to render some heavy UI all at once, which may cause jarring effects for the users. With React18, we can handle this by delaying the updates that are not urgent.

react 18 start transition

There are some other noticeable features in React18 that you might want to check out by playing with the linked sandboxes from React’s official website. See useDeferredValue and startTransition with Suspense.

Maintenance and support

The React Native core team is committed to offer support for the 3 latest versions of React Native (you can check the support policy) and the React core team plans new features built on the concurrent rendering engine. It’s important to not stay behind as the cost of paying the tech debt will get higher with time. It’s worth pointing out that React Native is no different from any other software project in this regard. Not updating dependencies may not only cause your team to spend more time on this task when it’s unavoidable. It can also expose your app to security vulnerabilities already patched in the upstream.

The React Native team has the dedicated capacity to help the community solve their app and library problems regarding new architecture adoption in close cooperation. Although it's not stable yet, it's worth trying out in your app today. Especially considering that since React Native v0.72 the Interop Layer exists which allows running most of the old architecture components with apps that enabled the New Architecture.

Need help with performance? Give us a shout!

If you’re struggling with improving your app performance, get in touch with us. Our React Native development company is an official Meta partner and community leader. We’ve delivered high-quality solutions for dozens of international clients, from startups to enterprises, and created some cool Open Source projects like Re.Pack or Reassure. Now’s the time we help your business grow.

Insights

Learn more about React Native

cover

Aug 4Podcast

VisionCamera V5 With Marc Rousavy

Marc Rousavy joins Mike Grabowski to unpack VisionCamera v5, Nitro Modules, the constraints API, migration work, and what changed for React Native camera apps.

cover

Jul 1Podcast

Redraw for Effects Beyond Skia

William Candillon talks about where Redraw fits next to Skia, from physics-based rendering to camera effects, AR, and new UI patterns.

cover

Jun 29Article

Less Boilerplate, More Type Safety: React Navigation 8's Typed Hooks

React Navigation 8 improves typed hooks with screen-aware types, less boilerplate, and fewer unsafe casts in shared UI and nested navigators.

cover

Apr 30Live Stream

Build a React Native App With the TanStack Stack

Join Callstack’s live stream on building a React Native app from the ground up using the TanStack toolbelt.

cover

Apr 28Article

React Native production lessons from React Universe Meetup x Zalando

Lessons from React Universe Meetup x Zalando on React Native in production, covering brownfield performance, feed video, native API access, and on-device AI.

cover

Apr 23Talk

Shipping iOS Live Activities with React: The Voltra Story

How Voltra enables Live Activities and Dynamic Island views using React without writing Swift. A deep dive into iOS constraints and architecture.

cover

Apr 16Live Stream

React Native 0.85: Animation Backend and Key Updates

A recap of React Native 0.85, covering the new animation backend, DevTools improvements, and ecosystem takeaways.

cover

Mar 5Article

Keeping Screens Visible With <Activity mode="hidden">

A cross‑platform workaround to keep screens visible while <Activity mode="hidden"> unmounts effects on inactive screens by setting mode="hidden".

cover

Feb 25Article

React Native Monorepo With pnpm Workspaces

Set up a React Native monorepo with pnpm workspaces for mobile and Next.js web, including Metro fixes, hoisted node-linker, and shared packages.

cover

Feb 18Article

A Practical Guide to React Native Monorepo With Yarn Workspaces

Learn how to set up a React Native monorepo with Yarn workspaces for mobile and web, including Metro, Gradle, and shared packages.

cover

Feb 17Podcast

From Teddy Bears to Voice Agents: Kraen Hansen on Voice AI, Local-First & App Security

What happens when you combine voice agents and teddy bears? A hands-on look at local-first systems, voice AI, and security lessons from poking real apps.

cover

Jan 28Article

Bringing CSS Clipping to React Native

A deep look at how a single CSS feature moves through React Native’s New Architecture, from JavaScript styles to native rendering on iOS and Android.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

cover

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.