React Native’s New Architecture

Saddam Husain
VectoScalar
Published in
7 min readDec 30, 2022

--

This article aims to cover the most important changes brought by the re-architecture:

  1. JavaScript Interface(JSI)
  2. Fabric
  3. Turbo Modules
  4. CodeGen

Current Architecture

Before we get to the new architecture, let’s recap how the current one works.

When you run a RN app, all your JavaScript code is bundled together into a package called the JS Bundle. The Native Code is kept separately.

The Execution of React Native apps happens over three threads:

1) The JavaScript thread: used by the JS Engine, to run the JS Bundle

2) The Native/UI thread: used to run the Native Modules and to handle operations like UI Rendering, user gesture events etc.

3) Additionally there is a 3rd thread called the shadow thread, which is used to calculate the Layout of Elements before rendering them on the host screen

The Communication between the JS and Native Threads is carried over an entity called the bridge. When sending data through the bridge it has to be batched(optimised) and serialised as JSON. This bridge can only handle asynchronous communication.

In the current architecture, React Native uses the Bridge Module to make communication possible between the JS and Native threads. Every time data is sent across the bridge, it has to be serialised as JSON. When the data is received on the other side it must be decoded as well.

This means that the JavaScript and Native worlds are unaware of each other (ie. the JS thread cannot directly call a method on the Native thread)

Another important point to note is; messages send over the bridge are asynchronous in nature, which is a good thing for most use cases, but there are certain instances when JS code and native code needs to be in sync.

Let’s take an example to better understand the bridge:

If the JavaScript thread needs access to some native modules (eg. Bluetooth), it will need to send a message to the native thread. The JS thread will send a serialised JSON message to the bridge. The bridge will optimize this message and send it over to the native thread. The message will be decoded on the native thread, and eventually the required native code will be executed.

However, in the New Architecture, the bridge is going to be replaced with a module called JavaScript Interface, which is a lightweight, general-purpose layer, written in C++ that can be used by the JavaScript engine to directly invoke/call methods in the native realm.

What does general-purpose mean?

The current architecture uses the JavaScriptCore Engine. The bridge is only compatible with this particular engine. However, this is not the case for JSI. The JavaScript Interface will be decoupled from the Engine, which means that the new architecture enables the use of other JavaScript Engines like Chakra, v8, Hermes etc. Hence the term “general-purpose”.

How can JSI enable JavaScript to directly call native methods?

Through the JSI, Native methods will be exposed to JavaScript via C++ Host Objects. JavaScript can hold a reference to these objects. And can invoke the methods directly using that reference. This is similar to the web, where JavaScript code can hold a reference to any DOM element, and call methods on it. For Example: when you write:

const container = document.createElement(‘div’);

Here, the container is a JavaScript variable, but it holds a reference to a DOM element which was probably initialized in C++. If we call any method on the “container” variable, it will in turn call the method on the DOM element. The JSI will work in a similar way.

Unlike the bridge, the JSI will allow JavaScript code to hold a reference to Native Modules. And through the JSI, JavaScript can call methods on this reference directly.

To Sum it up, JSI will enable the use of other JavaScript Engines & it will allow for complete interoperability between the threads, the JavaScript code could communicate with the native side directly from the JS thread. This will eliminate the need to serialize JSON messages and will fix the congestion and asynchronous issues on the bridge.

Another big advantage of the JSI is that it is written in C++. With the power of C++ React Native can target large number of Systems like Smart TVs, Watches etc.

2. Fabric

Fabric is the rendering system, which will replace the current UI Manager.

In order to understand the advantages of Fabric, first let’s look at how UI is currently rendered in React Native:

When your app is run, React executes your code and creates a ReactElementTree in JavaScript. Based on this tree, the Renderer creates a ReactShadowTree in C++.

This shadow Tree is used by the Layout Engine to calculate positions of UI elements for the host screen. Once the results of Layout calculation are available, the shadow tree is transformed into HostViewTree, which comprises of Native Elements. (For example: The ReactNative <View/> element will be translated into ViewGroup in Android & UIView in iOS respectively)

Problems with this approach:

As we know, all the communication between threads happens over the bridge. Which means slow transfer rates, and unnecessary copying of data.

For Example: If a ReactElementTree Node happens to be an <Image/>, then the consequent node of the ReactShadowTree will also be an image. But this data will have to be duplicated and stored separately in both the nodes.

That’s not all. Since the JS and UI threads are not in sync, there are certain use cases when your app can seem laggy as it drops frames. (Example: Scrolling through a FlatList with a huge list of data).

What is Fabric?

“Fabric is React Native’s new rendering system, a conceptual evolution of the legacy render system”

As we have seen in the JSI section of this article, the JavaScript Interface will directly expose native methods to JavaScript, which also includes UI methods. As a result of this, the JS and UI thread can be in sync. This will improve performance for lists, navigation, gesture handling etc.

What are the benefits of Fabric?

With the new rendering system, user interactions such as scrolling, gestures etc can be prioritized to be executed synchronously in the main thread or native thread. While other tasks such as API requests will be executed asynchronously.

That’s not all. The new Shadow Tree will be immutable, and it will be shared between the JS and UI threads, for allowing straight interaction from both ends.

As we have seen, in the current architecture React Native has to maintain two hierarchies/DOM nodes. But since the shadow tree will now be shared among realms, it will help with reducing memory consumption as well.

3. Turbo Modules

In the current architecture, all the Native Modules used by JavaScript (e.g. Bluetooth, Geo Location, File Storage etc) have to be initialized before the app is opened. This means, even if the user doesn’t require a particular module, it still has to be initialized at start-up.

Turbo Modules are basically an enhancement over these old Native modules. As we have seen in the previous part of this article, now JavaScript will be able to hold reference to these modules, which will allow JS Code to load each module only when it is required. This will significantly improve start-up time for ReactNative apps.

4. CodeGen

All this talk of Turbo Modules and Fabric sounds promising, but JavaScript is a Dynamically typed language, and JSI is written in C++, which is a Statically Typed Language. Consequently, there is a need to ensure smooth communication between the two.

That’s why the new architecture will also include a static type checker called CodeGen.

By using the typed JavaScript as the source of truth CodeGen will define interface elements used by Turbo Modules and Fabric. It will also generate more native code at build time, instead of run time.

Summary

If we combine all the changes, the new architecture will look like this:

Here are the key highlights :

• Bridge will be replaced by JSI

• Ability to swap the JavaScriptCore with other Engines

• Complete Interoperability between all threads

• Web-like Rendering system

• Time sensitive tasks can be executed synchronously

• Lazy Loading of Turbo Modules

• Static Type Checking for compatibility between JS and Native Side

We can be certain that this new structure will give React Native some powerful improvements.

--

--