Understanding the Inner Workings of React Native

React Native has become a powerful tool for creating cross-platform mobile apps using a single JavaScript/TypeScript codebase. With the modern architecture now standard — including JSI, Fabric, TurboModules, and the Hermes engine — the internal workings of React Native have changed significantly.

In this post, we’ll explore how React Native works under the hood in React Native 0.73+, and break down its internals using a real-world example.


High-Level Overview

React Native bridges two worlds:

  • JavaScript (JS): Where your app logic and UI declarations live.

  • Native (Android/iOS): Where actual UI components are rendered and device features accessed.

Originally, these two layers communicated through a bridge, which serialized data between them. But that model had major performance bottlenecks. Now, React Native's new architecture replaces the bridge with:

  • Hermes: An optimized JS engine built for mobile.

  • JSI (JavaScript Interface): Enables direct communication between JavaScript and native code via C++.

  • Fabric: A redesigned renderer aligned with React 18's concurrent rendering.

  • TurboModules: A new way of integrating native modules efficiently and lazily.


Real World Example: “Hello, World” with Style

Let's walk through what happens when you render a simple component:

import { Text, View, StyleSheet } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Hello, world!</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1, justifyContent: 'center', alignItems: 'center',
  },
  text: {
    fontSize: 24, color: 'blue',
  },
});

Now let’s peel back the layers.


Step-by-Step: What Actually Happens

1. JavaScript Execution via Hermes

When your app runs:

  • Metro Bundler compiles your JS and bundles it.

  • Hermes — React Native’s default JS engine — loads and executes this bundle.

  • Hermes optimizes startup with Ahead-of-Time (AOT) compilation, reducing parse and eval time.

2. JSX → React Elements

JSX gets compiled into function calls like:

React.createElement(View, { style: ... },
  React.createElement(Text, { style: ... }, "Hello, world!")
);

React constructs a virtual DOM tree — essentially, a lightweight JavaScript object representation of your UI.


3. Reconciliation + Scheduling

React 18's concurrent renderer kicks in:

  • React compares the new virtual tree to the previous one (if any).

  • It schedules updates using Concurrent Mode (e.g., via startTransition, Suspense).


4. Fabric Renderer (Shadow Tree)

This is where React Native's Fabric system steps in:

  • React Native’s renderer (react-reconciler) hands the React tree off to Fabric.

  • Fabric builds a shadow tree — a C++ representation of the UI layout.

  • The shadow tree is platform-agnostic and used to calculate layout.


5. Layout with Yoga

Fabric uses Yoga, a cross-platform layout engine (built in C++) to:

  • Compute the layout (position, size) of each element in the tree.

  • This layout is synchronous and handled off the JS thread, so no blocking.


6. Rendering Native Views

Once layout is computed:

  • The UIManager tells the platform (Android/iOS) to mount or update native views.

  • View maps to android.view.ViewGroup or UIView, and Text to TextView or UILabel.

At this point, you see the result on screen.


How Native Code is Accessed: TurboModules + JSI

Let’s say you use a module like react-native-device-info to get the device model.

import DeviceInfo from 'react-native-device-info';

const model = await DeviceInfo.getModel();

Under the hood:

  • TurboModule allows DeviceInfo to be lazily loaded only when getModel() is called.

  • Instead of going through a bridge (with JSON serialization), the call uses JSI to directly access the native C++ binding.

  • Result: faster execution, less memory usage.

You can even write your own TurboModules in C++ and expose them to JS via JSI.


Threading Model (Simplified)

React Native separates concerns using multiple threads:

ThreadPurpose
JS ThreadRuns your app logic (React, Redux, navigation, etc).
UI ThreadRenders views, handles user interaction.
Native Modules ThreadExecutes native async functions (e.g. network, sensors).

Heavy lifting (animations, layout, native access) can now happen off the JS thread, enabling better performance.


Events & Gestures

Gesture libraries like react-native-gesture-handler or Reanimated 3 now work natively with Fabric.

Example: A pan gesture to drag a card.

  • Gesture is recognized and processed on the native side.

  • Updates (e.g., translation values) are passed via worklets to keep logic off the JS thread.

  • No jank, no delays.


Modern Debugging Tools

React Native 2025 ships with powerful tooling:

  • Flipper: Inspect layout, logs, network, and React DevTools.

  • Hermes Profiler: Trace JavaScript execution and performance bottlenecks.

  • React DevTools: View component tree, props, and hooks.


Performance Tips (Real-World Ready)

  • Enable Hermes and Fabric in production builds.

  • Use FlashList instead of FlatList for large lists.

  • Offload CPU-heavy work to native modules or background threads.

  • Prefer JSI-based libraries (e.g., react-native-mmkv for storage).

  • Lazy-load modules and screens using dynamic imports and TurboModules.


Summary: What Makes the New Architecture Special

Feature

Legacy

New (2025)

Communication

Bridge (slow)

JSI (fast)

Rendering

Native Modules

Fabric

Native APIs

NativeModules

TurboModules

JS Engine

Optional

Hermes (default)

Threading

Mostly JS thread

Multi-threaded, concurrent

Performance

Bottlenecks

Optimized


Conclusion

React Native's modern architecture represents a significant leap forward in the development of cross-platform mobile applications. By leveraging tools like JSI, Fabric, TurboModules, and Hermes, developers can create apps that are faster, more efficient, and capable of handling complex UI and native interactions seamlessly. Understanding these under-the-hood changes empowers developers to fully harness the potential of React Native 0.73+ and beyond. As the ecosystem continues to evolve, embracing the new architecture will be key to building high-performance, scalable, and future-proof applications.

0
Subscribe to my newsletter

Read articles from Leonardo Fernandes directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Leonardo Fernandes
Leonardo Fernandes