# How React Native Implements Its JavaScript to Native Bridge

> Discover how React Native implements its JavaScript to Native bridge. Explore the three-layer architecture, JS executor, and C++ registry for efficient bidirectional calls.

- Repository: [Meta/react-native](https://github.com/facebook/react-native)
- Tags: internals
- Published: 2026-02-25

---

**React Native's JavaScript-to-Native bridge operates through a three-layer architecture comprising a platform-specific Bridge object, a JavaScript executor running Hermes or JSC, and a C++ ModuleRegistry that marshals bidirectional calls via the `facebook::react::Instance` class.**

The JavaScript-to-Native bridge serves as the core communication layer in React Native, enabling JavaScript code to invoke platform-specific methods written in Objective-C, Swift, Java, or Kotlin. According to the `facebook/react-native` source code, this bridge relies on a shared C++ runtime wrapped by platform-specific APIs, allowing seamless interoperability while maintaining type safety and performance across iOS and Android platforms.

## The Three-Layer Bridge Architecture

The bridge implementation divides responsibilities across three distinct layers that work in concert to mediate between JavaScript and native code.

The **Bridge object** provides the public API that applications interact with directly. On iOS, this is `RCTBridge` (defined in [`React/Base/RCTBridge.h`](https://github.com/facebook/react-native/blob/main/React/Base/RCTBridge.h)), while Android uses `CatalystInstanceImpl` (located in [`ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp`](https://github.com/facebook/react-native/blob/main/ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp)). This layer creates the JavaScript thread, instantiates the C++ runtime, and manages the lifecycle of native modules.

The **JavaScript executor** runs the application bundle using engines like Hermes or JavaScriptCore. The executor exposes `callJSFunction` and `callJSCallback` interfaces that allow the bridge to forward native-to-JavaScript calls. Key implementations include [`JSIExecutor.cpp`](https://github.com/facebook/react-native/blob/main/JSIExecutor.cpp) (in `ReactCommon/jsiexecutor/jsireact/`) and [`HermesExecutorFactory.h`](https://github.com/facebook/react-native/blob/main/HermesExecutorFactory.h) (in `ReactCommon/hermes/executor/`).

The **Module registry and native-module proxy** layer converts calls between the two worlds. The C++ `Instance` class owns a `ModuleRegistry` (implemented in [`ReactCommon/cxxreact/ModuleRegistry.cpp`](https://github.com/facebook/react-native/blob/main/ReactCommon/cxxreact/ModuleRegistry.cpp)) containing C++ wrappers (`JSINativeModules`) around each native module. When JavaScript calls `NativeModules.MyModule.myMethod()`, this layer creates call objects containing `[moduleId, methodId, args, callId]` and routes them through the appropriate platform channels.

## Core Bridge Components

### The Bridge Object

The bridge object serves as the primary entry point for both platforms. On iOS, the concrete implementation `RCTCxxBridge` (found in `React/CxxBridge/RCTCxxBridge.mm`) handles the bridge lifecycle. During its `start` method, it spins up the JavaScript thread, instantiates the C++ `facebook::react::Instance` stored as `_reactInstance`, and builds a `JSExecutorFactory` (Hermes by default) passed to `Instance::initializeBridge`.

On Android, `CatalystInstanceImpl` manages the same responsibilities through JNI, holding a `std::unique_ptr<Instance>` and registering native modules via `JavaModuleWrapper` classes.

### The JavaScript Executor

The executor layer initializes the JavaScript runtime and processes the application bundle. When `RCTCxxBridge` starts without a specified executor class, it defaults to `HermesExecutorFactory`, which creates a `JSIExecutor` instance. This executor installs JavaScript interface (JSI) bindings that allow direct memory sharing between C++ and JavaScript, eliminating serialization overhead for certain operations.

The executor processes calls through `JSIExecutor::callFunction`, which the C++ `Instance` invokes when native code needs to trigger JavaScript functions.

### The Module Registry

The `ModuleRegistry` maintains all registered native modules in a centralized C++ registry. When JavaScript accesses `NativeModules`, the `JSINativeModules` class (implemented in [`ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp`](https://github.com/facebook/react-native/blob/main/ReactCommon/jsiexecutor/jsireact/JSINativeModules.cpp)) resolves module and method IDs.

On iOS, `RCTModuleData` (defined in [`React/Base/RCTModuleData.h`](https://github.com/facebook/react-native/blob/main/React/Base/RCTModuleData.h)) wraps concrete module classes conforming to the `RCTBridgeModule` protocol, lazily creating instances and recording method maps. The C++ side mirrors this structure, ensuring that calls from `Instance::callJSFunction` resolve to the correct native implementation.

## JavaScript-to-Native Call Flow

The path from JavaScript to native code follows a precise sequence through the bridge layers.

1. **JavaScript generates the call.** When code executes `NativeModules.MyModule.doSomething('hello')`, the batched bridge in [`Libraries/BatchedBridge/NativeModules.js`](https://github.com/facebook/react-native/blob/main/Libraries/BatchedBridge/NativeModules.js) creates a call object containing the module ID, method ID, arguments, and call ID. This object pushes onto a message queue monitored by the bridge.

2. **The bridge receives and forwards the call.** On iOS, `RCTCxxBridge` receives the call via `enqueueJSCall:` and forwards it to the C++ instance:

   ```objc
   [self _runAfterLoad:^{
     strongSelf->_reactInstance->callJSFunction(
        [module UTF8String], [method UTF8String],
        convertIdToFollyDynamic(args ?: @[]));
   }];
   ```

3. **The C++ runtime resolves the module.** `Instance::callJSFunction` looks up the target module in its `ModuleRegistry` using `JSINativeModules`, finds the matching method via the recorded method map, and invokes the Objective-C implementation through `RCTModuleData.instance` or the Android `JavaModuleWrapper` equivalent.

## Native-to-JavaScript Call Flow

Native modules communicate back to JavaScript using the bridge's callback APIs. Native code calls `enqueueJSCall:` on iOS:

```objc
[self.bridge enqueueJSCall:@"RCTDeviceEventEmitter"
                    method:@"emit"
                      args:@[ @"MyEvent", payload ]
                completion:nil];

```

This call queues on the JavaScript thread. When the `JSIExecutor` processes the queue, it executes the corresponding JavaScript function (such as `RCTDeviceEventEmitter.emit`), delivering the native event to JavaScript listeners. On Android, the JNI method `jniCallJSCallback` performs the equivalent function, forwarding native calls to `Instance::callJSCallback`.

## Platform-Specific Implementations

### iOS Bridge Architecture

iOS implements the bridge through Objective-C++ wrappers around the C++ core. [`RCTBridge.h`](https://github.com/facebook/react-native/blob/main/RCTBridge.h) defines the public API, while `RCTCxxBridge.mm` contains the concrete implementation managing the JavaScript thread and C++ `Instance`. Native modules implement the `RCTBridgeModule` protocol from [`React/Base/RCTBridgeModule.h`](https://github.com/facebook/react-native/blob/main/React/Base/RCTBridgeModule.h), with the bridge building a registry of `RCTModuleData` objects that handle lazy instantiation and method resolution.

### Android Bridge Architecture

Android bridges Java and C++ through JNI using [`CatalystInstanceImpl.cpp`](https://github.com/facebook/react-native/blob/main/CatalystInstanceImpl.cpp). This class registers native modules wrapped in `JavaModuleWrapper` objects and forwards Java-side calls to the C++ `Instance::callJSFunction` via `jniCallJSFunction`. The architecture mirrors iOS but adapts to Android's JNI requirements, maintaining the same three-layer separation between Java, C++, and JavaScript execution contexts.

## TurboModules and Bridgeless Mode

When the TurboModule feature flag is enabled, React Native enters a "bridgeless" mode that bypasses the legacy `RCTBatchedBridge` message queue. In this configuration, native modules register directly with the C++ `ModuleRegistry` through the `TurboModuleRegistry`, eliminating the overhead of batched call serialization.

The same `facebook::react::Instance` class continues to mediate calls, but lookup occurs directly via C++ module proxies rather than through `RCTModuleData` or `JavaModuleWrapper`. The public JavaScript API remains unchanged, ensuring backward compatibility while improving performance through synchronous native method calls where appropriate.

## Summary

- The bridge architecture consists of three layers: the platform-specific Bridge object (`RCTBridge`/`CatalystInstanceImpl`), the JavaScript executor (`JSIExecutor` with Hermes/JSC), and the C++ ModuleRegistry with `JSINativeModules` proxies.
- Bidirectional communication flows through the C++ `facebook::react::Instance` class using `callJSFunction` for native-to-JS calls and `callJSCallback` for JS-to-native callbacks.
- iOS implements the bridge via `RCTCxxBridge.mm` and `RCTModuleData`, while Android uses [`CatalystInstanceImpl.cpp`](https://github.com/facebook/react-native/blob/main/CatalystInstanceImpl.cpp) with JNI and `JavaModuleWrapper`.
- TurboModules provide a bridgeless optimization path that registers C++ modules directly with the `ModuleRegistry`, bypassing legacy message batching while maintaining the same core mediation layer.

## Frequently Asked Questions

### How does the React Native bridge handle asynchronous operations between JavaScript and native code?

The bridge operates on a dedicated JavaScript thread separate from the main UI thread. Native modules can expose asynchronous methods using promises or callbacks, with the bridge queueing calls via `enqueueJSCall:` on iOS or `jniCallJSFunction` on Android. The C++ `Instance` class ensures thread-safe marshaling of arguments and return values between the JS executor and native module implementations.

### What is the difference between the legacy bridge and TurboModules?

The legacy bridge uses a batched message queue where calls serialize into `[moduleId, methodId, args]` arrays processed by [`Libraries/BatchedBridge/NativeModules.js`](https://github.com/facebook/react-native/blob/main/Libraries/BatchedBridge/NativeModules.js). TurboModules eliminate this batching overhead by registering C++ modules directly with the `ModuleRegistry`, enabling synchronous calls and reducing marshaling latency. Both architectures use the same `facebook::react::Instance` class for core mediation, but TurboModules bypass the Objective-C `RCTModuleData` and Java `JavaModuleWrapper` proxies when possible.

### Can developers use JavaScript engines other than Hermes with the React Native bridge?

Yes, the bridge supports multiple JavaScript engines through the `JSExecutorFactory` interface. While Hermes is the default (configured in [`HermesExecutorFactory.h`](https://github.com/facebook/react-native/blob/main/HermesExecutorFactory.h)), the bridge can instantiate JavaScriptCore or V8 engines by providing alternative factory implementations to `RCTCxxBridge` on iOS or `CatalystInstanceImpl` on Android. The `JSIExecutor` (in [`JSIExecutor.cpp`](https://github.com/facebook/react-native/blob/main/JSIExecutor.cpp)) abstracts engine-specific details, providing a uniform interface for the C++ `Instance` to execute calls.

### How are native module methods exposed to JavaScript in the bridge?

Native modules implement platform-specific protocols (`RCTBridgeModule` on iOS or `ReactContextBaseJavaModule` on Android) and use macros like `RCT_EXPORT_METHOD` or `@ReactMethod` to annotate methods. The bridge scans these annotations during initialization, building method maps in `RCTModuleData` (iOS) or `JavaModuleWrapper` (Android). The `JSINativeModules` C++ class then exposes these methods to JavaScript through the JSI layer, allowing direct invocation via the `NativeModules` JavaScript API.