How does React Native work? Understanding the architecture

Jakub Kosmal
Frontend Weekly
Published in
6 min readNov 11, 2022

What is React Native?

React Native is a great piece of technology that allows us to write natively rendered iOS and Android apps using React and JavaScript.

There are two main advantages to that approach. Firstly, it makes it very easy for web developers to write mobile applications that feel native using the most popular JavaScript UI library.

Secondly, most of the code that is written in React Native, can be shared between platforms which simplify simultaneous development for both iOS and Android.

So, how does it work?

The idea behind React Native is to combine two separate pieces — JavaScript code and Native code (Java/Kotlin for Android and Objective-C/Swift for iOS) and make them work together. While native code will be executed directly on the device, JavaScript needs a virtual machine to be run on.

IOS devices have a built-in JavaScript engine called JavaScriptCore written in C++, which will compile and execute our JavaScript code. Android devices don’t have a built-in JS engine, so JavaScriptCore will be brought along with the React Native framework.

Since Java/Obj-C and JavaScript are different programming languages, they cannot speak directly to each other. However, they can speak indirectly by using a form of data that they both understand, and that would be JSON. This communication is handled by a set of programs called Bridge.

React Native’s architecture

React Native’s build and runtime

During build time, native code written in Java or Objective-C will be compiled into Java and C++ binary files and JavaScript code will be bundled using the Metro bundler. Metro works similarly to the Webpack bundler used in web development but it’s optimized for React Native.

Binaries and JS bundle will eventually be packed inside an executable file for targeted platform.

At runtime, the JavaScript code will run on the JavaScript VM, and native code will run directly on the device. The Bridge will transfer serialized messages between those two realms. These messages will then be deserialized and processed.

React Native’s Threads

When a user runs an application, the device will start three main threads, as well as additional background threads if needed.

RN’s threads

Main Thread — this is the main native thread on which the application will be running. It is responsible for user interactions and rendering UI on the screen of a device. It is the same thread that is running in every fully natively built application.

JavaScript Thread — this is where the business logic of the application will be executed (i.e., JavaScript and React code).

Shadow Thread — this thread will be launched along the JavaScript thread. Its responsibility is to compute the positions of the views and construct a tree of layout coded in the JS thread. React Native utilizes a layout engine called Yoga which converts flexbox-based layout into a layout system that a native host can understand.

Native Modules Thread — when the application needs access to a platform API, like, for example, the device’s storage, this thread will handle it.

React Native’s Bridge

The Bridge is, without a doubt, the most important part of React Native’s architecture. It allows us to exchange information between the JavaScript and Native layers through JSON messages.

The logic is the same as in the case of web applications, where the frontend and backend layers do not need to know anything about each other (and thus can be written in different technologies, like, for example, JavaScript and Python), but they must understand the information they exchange to process it.

Let’s walk through an example

Bridge data flow example
  1. A native event occurs. For example, touch or scrolling event.
  2. The serialized message is sent from the native side through the bridge with all the necessary data.
  3. JavaScript receives the message, deserializes it and decides what to do next. In that case, changing icon
  4. The message is sent from the JavaScript layer through the bridge with information about the requested action.
  5. The native side receives the message, deserializes it and updates the view.

The problem with the bridge

As mentioned above, the bridge is asynchronous, which leads to a problem that may appear with edge cases.

While exchanging information async through the bridge is very fast, sometimes it’s not enough, and the synchronous way would be better.

Suppose we have an input box where we want a user to type his credit card number. We want to insert space every four digits to make reading numbers easier. To make that happen, we’d write a simple JS function that would be invoked every time a user types a character.

Let’s see how that would work

As you can see, the information from the native side about what character the user typed will come after this character has already been rendered. Then, our JavaScript code will handle that change by inserting space if needed and setting our new input state, which will result in sending this information back to the native side and rendering a new UI.

This may lead to a situation where a user will see text before it has been formatted and updated (i.e., “42578" instead of “4257 8”). If this would be the only thing that bridge was processing at the time, that jumps of the state wouldn’t be noticeable, because of how fast this operation would happen. However, dealing with huge number of updates at the same time can cause traffic, just like with real life bridges and you can imagine that this could be a problem in a complex application.

Another issue with the bridge is that the information that is exchanged between JavaScript and Native realms must be serialized and then deserialized, which is time-consuming, and again, as the application grows, it can make a difference.

To tackle these problems, the React Native team found a better and more efficient way to combine JavaScript with native code and introduced the JavaScript Interface into their new architecture.

JSI — JavaScript Interface

Starting from version 0.68, it’s possible to use the new React Native architecture, which drops the Bridge mechanism in favor of the JavaScript Interface.

JSI is a unified, lightweight, general-purpose layer that could be utilized in any JavaScript engine. By using it, we can finally make direct connections to native APIs.

But how?

C++ will expose native Java/Obj-C methods/objects to JavaScript via the “HostObject”. JavaScript will hold a reference to this object, which will enable it to access methods and props directly on Java/Obj-C API.

This can be done synchronously on the same thread or asynchronously by creating a new thread.

This basically takes React Native to a whole new level and will soon be widely adopted because of the great advantages it provides.

If you want to dive deeper into this topic, I encourage you to watch a presentation of the new architecture by Parashuram N and refer to the official React Native docs.

Conclusion

You should now have a brief overview of how React Native works and what its architecture looks like. I hope you found this article helpful and that it encouraged you to explore the world of React Native further.

--

--