---
title: Deep Dive into React Native New Architecture: Nitro Modules vs. TurboModules for High-Throughput C++ Interop
publishedAt: 2026-08-29
summary: An architectural analysis comparing Nitro Modules, standard TurboModules, and CxxTurboModules in the React Native New Architecture, examining JSI dispatch, Swift 5.9+ C++ interop, memory semantics, and buffer throughput.
---

# Deep Dive into React Native New Architecture: Nitro Modules vs. TurboModules for High-Throughput C++ Interop

**Executive Summary (TL;DR):**  
The **React Native New Architecture** replaced the asynchronous, JSON-serialized bridge with the **JavaScript Interface (JSI)**, enabling direct, synchronous C++ host function execution. While standard **TurboModules** satisfy general platform API requirements, compute-intensive domains—such as real-time audio digital signal processing (DSP), computer vision frame processing, and on-device ML/vector inference—frequently encounter latency and allocation bottlenecks due to platform-specific marshalling layers (JNI on Android, dynamic Objective-C runtime dispatch via `objc_msgSend` on iOS). **Nitro Modules** mitigate these intermediary layers by introducing a unified `HybridObject` abstraction, compile-time binding generation via Nitrogen, native Swift 5.9+ bidirectional C++ interoperability, and direct `ArrayBuffer` memory mapping.

---

## Architectural Foundations: JSI and the Native Interop Paradigm Shift

The React Native New Architecture replaces the legacy asynchronous, serialized JSON bridge with the C++ JavaScript Interface (JSI). JSI enables the JavaScript engine to hold direct references to host C++ objects and invoke native methods synchronously through function pointers. This foundational shift eliminates message queue serialization latency and provides direct memory access primitives for native modules.

For years, React Native relied on an asynchronous, batched JSON bridge. This transport layer introduced three structural architectural constraints:

1. **Serialization Bottlenecks:** Every cross-boundary function invocation required serializing arguments into JSON strings on the JavaScript thread and deserializing them on the native platform thread.
2. **Asynchronous Dispatch Latency:** JavaScript could not query native state synchronously; all state queries required asynchronous promises or batched callback queues, making 60/120 Hz synchronized frame pipelines difficult to maintain.
3. **Intermediate Heap Allocations:** Binary payloads (such as raw pixel buffers, sensor streams, and cryptographic keys) underwent multiple defensive allocations across the JavaScript engine heap, C++ core, and native platform runtimes (JVM/Objective-C heap).

```
[Legacy Bridge Architecture]
JS Engine (Hermes/V8) <─── JSON Serialization ───> Bridge Message Queue <─── Deserialization ───> Native (Java/Obj-C)
```

The [React Native New Architecture documentation](https://reactnative.dev/docs/the-new-architecture/landing-page) establishes that the transport layer bottleneck is resolved through the **C++ JavaScript Interface (JSI)**. JSI is a lightweight, engine-agnostic C++ abstraction layer that enables the JavaScript runtime (Hermes, V8, JavaScriptCore) to hold direct references to host C++ objects (`jsi::HostObject`) and invoke host functions directly via function pointers.

```
[New Architecture with JSI]
JS Engine (Hermes) <═════════════════ JSI (Direct C++ Function Pointers) ═════════════════> Native C++ Core
```

JSI provides the underlying engine primitives, while native module frameworks—**TurboModules** and **Nitro Modules**—define how developers design, compile, and execute native capabilities.

---

## TurboModules Architecture and Execution Pipeline

TurboModules are the official React Native Core standard for typed native module integration via Meta's `react-native-codegen`. While they provide synchronous JSI execution, platform TurboModules introduce intermediate translation layers such as JNI on Android and dynamic Objective-C dispatch on iOS. Pure CxxTurboModules bypass these runtime abstractions but require extensive C++ boilerplate and lack native Swift or Kotlin ergonomics.

```
TypeScript Spec (.ts)
       │
   [Codegen]
       ├──> C++ JSI Scaffolding (Spec.h, Spec.cpp)
       ├──> iOS: Objective-C++ Wrapper (RCTTurboModule)
       └──> Android: JNI Scaffolding + Java/Kotlin Abstract Base
```

### The Platform TurboModule Execution Flow

When JavaScript invokes a method on a standard platform TurboModule:

1. **JSI Host Function Invocation:** The JavaScript engine calls the corresponding C++ JSI host function registered during module initialization.
2. **Argument Unpacking:** The C++ layer unpacks `jsi::Value` instances into intermediary representations (`folly::dynamic`, `std::string`, `std::vector`, or scalar primitives).
3. **Platform Marshalling and Dispatch:**
   - **iOS:** The C++ layer invokes the native method via an Objective-C++ (`.mm`) adapter (`RCTTurboModule`). For Swift implementations, calls must be routed through `@objc` dynamic dispatch (`objc_msgSend`), requiring Swift classes to inherit from `NSObject`.
   - **Android:** The call crosses the Java Native Interface (JNI) boundary, converting C++ types into Java/Kotlin types (`jstring`, `jobject`, `ReadableMap`, `ReadableArray`).
4. **Return Value Conversion:** The native result is wrapped in platform types, marshalled back across JNI or Objective-C to C++, converted into a `jsi::Value`, and returned to the JavaScript execution context.

```
[Standard Platform TurboModule Call Chain]
JS -> JSI HostFunction -> C++ Codegen Spec -> JNI / Obj-C++ Bridge -> Kotlin / Swift Implementation
```

### Performance Characteristics and Bottlenecks in High-Throughput Scenarios

While TurboModules eliminate JSON serialization and bridge queue latency, platform-bridged TurboModules introduce overhead in high-throughput workloads:

- **JNI Boundary Crossing (Android):** Invoking Java/Kotlin from C++ requires JNI lookups and local reference table allocations. Under high invocation frequencies (e.g., continuous sensor streaming or audio DSP callbacks), JNI call overhead and object allocation introduce measurable CPU time and trigger ART garbage collection cycles.
- **Dynamic Type Marshalling:** Complex structured types often traverse intermediary data structures (such as `folly::dynamic` or `ReadableNativeMap`), resulting in multiple heap allocations and defensive memory copies.
- **Dynamic Objective-C Dispatch (iOS):** Because standard TurboModules rely on Objective-C interfaces, Swift classes must expose methods via `@objc`. This prevents direct C++ struct passing and disables compiler-level method inlining.
- **CxxTurboModules Complexity:** React Native supports pure C++ TurboModules (`CxxTurboModule`) that bypass JNI and Objective-C runtime overhead. However, authoring CxxTurboModules requires writing raw C++ against internal React Native headers (`ReactCommon`), maintaining cross-platform build scripts, and manually implementing custom bridging when platform-native APIs (Swift/Kotlin) are required.

---

## Nitro Modules: Architecture and Interop Mechanics

Nitro Modules is a high-throughput native interop framework built on JSI `HybridObject` abstractions, developed by Marc Rousavy and Margelo. By leveraging the Nitrogen CLI, it compiles TypeScript definitions directly into inlined C++ dispatchers, native Kotlin/JNI bindings, and bidirectional Swift 5.9+ C++ interop layers. This design minimizes dynamic runtime dispatch and provides direct pointer access to JavaScript memory buffers.

Refer to the official [Nitro Modules documentation](https://nitro.margelo.com/) and [Nitro Modules GitHub repository](https://github.com/mrousavy/nitro) for framework specifications and toolchain details.

```
[Nitro Modules Architecture]
TypeScript Spec (.nitro.ts)
       │
   [Nitrogen CLI]
       ├──> C++ JSI Dispatchers & Type-Safe Structs (Spec.hpp, Spec.cpp)
       ├──> iOS: Direct Swift 5.9+ C++ Interop Bindings (No Obj-C runtime)
       └──> Android: Direct C++ Core or Generated JNI / Direct ByteBuffer Bindings
```

### Key Architectural Mechanisms

#### 1. Inlined Compile-Time JSI Dispatch via Nitrogen
Instead of relying on generic runtime reflection or multi-layered wrapper classes, Nitrogen compiles TypeScript definitions into statically typed C++ structs and inlined JSI host functions. Type conversions between `jsi::Value` and native C++ types (`std::string`, `std::optional`, `std::vector`, `std::variant`) are direct and inlined at compile time.

#### 2. Direct Swift 5.9+ Bidirectional C++ Interoperability
Nitro Modules leverage the native C++ interoperability introduced in Swift 5.9 (detailed in the [Swift C++ Interoperability documentation](https://www.swift.org/documentation/cxx-interop/)). Instead of routing calls through Objective-C (`objc_msgSend`), Nitrogen generates C++ header bridges that Swift imports directly:
- Swift classes implement pure C++ `HybridObject` interfaces.
- Methods are invoked via direct function pointers or vtables without `@objc` or `NSObject` runtime overhead.
- Native C++ structs map directly to Swift structs without intermediate translation layers.

#### 3. Direct Memory Sharing with `ArrayBuffer`
For binary data handling (such as camera frame buffers, audio PCM streams, and neural network tensors), Nitro Modules provide typed memory wrappers around `jsi::ArrayBuffer`. C++ and Swift can obtain direct pointer access (`uint8_t*` or `UnsafeMutablePointer<UInt8>`) to the underlying memory block allocated by the JavaScript engine, avoiding defensive memory duplication.

#### 4. Android Optimization Paths
On Android, Nitro Modules support two execution models:
- **Pure C++ Execution:** The native logic executes entirely within the C++ layer via JSI, bypassing JNI completely.
- **Optimized JNI / Direct Buffers:** When interacting with Kotlin or Android platform SDKs, Nitro uses direct native memory mapping and direct `java.nio.ByteBuffer` instances, minimizing JNI object allocation.

---

## Comprehensive Comparison: Nitro Modules vs. TurboModules

Evaluating Nitro Modules against standard TurboModules and CxxTurboModules requires balancing dispatch latency, memory access patterns, and developer ergonomics. While standard TurboModules serve general platform SDKs well, Nitro Modules and CxxTurboModules excel in compute-heavy scenarios by bypassing dynamic runtime dispatch. The matrix below contrasts their implementation layers, data marshalling mechanics, and memory handling capabilities.

| Architectural Feature | TurboModules (Standard Platform) | TurboModules (CxxTurboModule) | Nitro Modules |
| :--- | :--- | :--- | :--- |
| **Primary Implementation Layer** | Objective-C++ (iOS) / Java-JNI (Android) | Pure C++ | Pure C++, Swift 5.9+, or Kotlin |
| **Swift Interoperability** | Indirect via `@objc` & `NSObject` (`objc_msgSend`) | Manual C++ wrapper required | Direct native Swift 5.9+ C++ interop (vtable / direct call) |
| **Kotlin / Java Interop** | Standard JNI + `ReadableMap`/`WritableMap` | Manual JNI implementation required | Generated fast JNI bindings / NIO buffers |
| **Codegen Tooling** | `react-native-codegen` (Meta Core) | `react-native-codegen` | `nitrogen-cli` (Margelo) |
| **Data Marshalling** | `jsi::Value` $\to$ C++ $\to$ Obj-C/JNI $\to$ Java/Swift | `jsi::Value` $\to$ C++ types | Direct `jsi::Value` $\to$ C++ structs / Swift types |
| **Binary Memory (`ArrayBuffer`)** | Requires manual C++ JSI handling | Direct via `jsi::ArrayBuffer` | Direct pointer access via typed buffer wrappers |
| **Build System Dependency** | Coupled to React Native Core build pipeline | Coupled to React Native Core build pipeline | Lightweight, standalone CMake / Podspec setup |
| **Primary Use Cases** | Standard platform APIs, UI modules, SDKs | Cross-platform C++ libraries, core RN engines | High-frequency data streams, Vision, DSP, ML |

---

## Memory Semantics, Allocation Patterns, and Concurrency

High-throughput mobile workloads are primarily constrained by garbage collection churn and defensive memory copying rather than raw instruction count. Nitro Modules allow direct pointer mapping into JavaScript `ArrayBuffer` instances, preventing the intermediate heap allocations common in platform TurboModules. However, working with raw shared buffers requires strict enforcement of buffer lifetime invariants and explicit thread synchronization.

```
[Memory Access Patterns]

TurboModules (Standard Platform):
[JS Heap: ArrayBuffer] ──(Copy)──> [C++ Intermediate Buffer] ──(Copy)──> [JVM/Obj-C Heap]

Nitro Modules (Direct Pointer Access):
[JS Heap: ArrayBuffer]
         ▲
         │ (Direct Pointer / ArrayBufferHolder)
[C++ / Swift / Kotlin Direct Buffer Access]
```

### 1. Heap Allocation and Garbage Collection Pressure
- **Platform TurboModules:** Passing nested dictionaries or complex payload arrays involves boxing and unboxing intermediate objects (`folly::dynamic`, `NSDictionary`, `ReadableNativeMap`). In high-frequency loops (e.g., 60–120 calls per second), these temporary allocations create high object churn on both the JavaScript heap and the JVM/Obj-C runtime, increasing the frequency of Garbage Collection (GC) pauses.
- **Nitro Modules:** Parameter conversions resolve directly into contiguous stack-allocated C++ structs or pre-allocated native heap structures. Eliminating intermediate wrapper objects significantly reduces GC pressure on the Hermes engine and the JVM.

### 2. Contiguous Memory Management and Safety
Direct memory access through `ArrayBuffer` requires careful adherence to lifetime and concurrency rules:
- **Buffer Lifetime:** A native pointer obtained from `jsi::ArrayBuffer::data()` remains valid only as long as the underlying JavaScript `ArrayBuffer` is kept alive and has not been detached or resized by the JavaScript engine.
- **Thread Affinity:** JSI calls execute synchronously on the thread from which they are called (typically the JavaScript thread). If native processing occurs on a background worker thread (e.g., for audio DSP or frame rendering), the buffer must either be copied or access must be explicitly synchronized to prevent data races.

---

## Practical Implementation: High-Throughput Frame Processor

Building a high-throughput image frame processor illustrates how Nitro Modules achieve direct contiguous buffer access across JavaScript, C++, and Swift. By defining a typed specification in TypeScript, Nitrogen generates the necessary C++ interfaces and Swift bindings to manipulate raw pixel buffers in-place. The following example demonstrates end-to-end configuration and memory-efficient pixel transformation without intermediate object allocation.

### 1. Define the Specification (`ImageProcessor.nitro.ts`)

```typescript
import { type HybridObject } from 'react-native-nitro-modules';

export interface ImageProcessorSpec extends HybridObject<{ ios: 'swift', android: 'c++' }> {
  /**
   * Synchronous scalar operation for algorithm configuration
   */
  configureFilter(filterType: string, intensity: number): boolean;

  /**
   * Direct pointer processing over contiguous memory
   */
  processFrameBuffer(buffer: ArrayBuffer, width: number, height: number): void;
}
```

### 2. Generate Scaffolding via Nitrogen

Execute the Nitrogen CLI to generate C++ interfaces and Swift/Kotlin bindings:

```bash
npx nitrogen-cli
```

Nitrogen outputs:
- `HybridImageProcessorSpec.hpp` (Abstract C++ interface)
- `HybridImageProcessorSpec.cpp` (Inlined JSI call dispatchers)
- `HybridImageProcessorSpec-Swift-Cxx-Bridge.hpp` (Swift-C++ interop bridge)

### 3. C++ Implementation (`HybridImageProcessor.hpp` / `.cpp`)

```cpp
#pragma once
#include "HybridImageProcessorSpec.hpp"
#include <algorithm>
#include <cstdint>

namespace margelo::nitro::imageprocessing {

class HybridImageProcessor : public HybridImageProcessorSpec {
public:
  HybridImageProcessor() : HybridObject(TAG) {}

  bool configureFilter(const std::string& filterType, double intensity) override {
    currentFilter_ = filterType;
    intensity_ = std::clamp(intensity, 0.0, 1.0);
    return true;
  }

  void processFrameBuffer(const std::shared_ptr<ArrayBuffer>& buffer, double width, double height) override {
    if (!buffer) {
      return;
    }

    // Direct pointer access to the JS ArrayBuffer memory block
    uint8_t* data = buffer->data();
    size_t size = buffer->size();

    // In-place pixel manipulation (Luminance grayscale transformation)
    for (size_t i = 0; i + 3 < size; i += 4) {
      uint8_t r = data[i];
      uint8_t g = data[i + 1];
      uint8_t b = data[i + 2];
      
      // Standard luminance weighting
      uint8_t gray = static_cast<uint8_t>(0.299 * r + 0.587 * g + 0.114 * b);
      
      data[i]     = gray;
      data[i + 1] = gray;
      data[i + 2] = gray;
      // Alpha channel at data[i + 3] remains unchanged
    }
  }

private:
  static constexpr auto TAG = "ImageProcessor";
  std::string currentFilter_ = "grayscale";
  double intensity_ = 1.0;
};

} // namespace margelo::nitro::imageprocessing
```

### 4. Swift Implementation via Swift 5.9+ C++ Interop (`HybridImageProcessor.swift`)

On iOS, the Swift class implements the generated C++ `HybridObject` interface directly without Objective-C bridging:

```swift
import Foundation
import NitroModules

public class HybridImageProcessor: HybridImageProcessorSpec {
    private var filterType: String = "grayscale"
    private var intensity: Double = 1.0

    public func configureFilter(filterType: String, intensity: Double) throws -> Bool {
        self.filterType = filterType
        self.intensity = max(0.0, min(1.0, intensity))
        return true
    }

    public func processFrameBuffer(buffer: ArrayBufferHolder, width: Double, height: Double) throws {
        // Obtain direct unsafe mutable pointer to the underlying buffer
        let pointer: UnsafeMutablePointer<UInt8> = buffer.data
        let totalBytes = buffer.size

        // In-place processing via Swift pointer arithmetic
        for i in stride(from: 0, to: totalBytes - 3, by: 4) {
            let r = Double(pointer[i])
            let g = Double(pointer[i + 1])
            let b = Double(pointer[i + 2])
            
            let gray = UInt8(0.299 * r + 0.587 * g + 0.114 * b)
            
            pointer[i] = gray
            pointer[i + 1] = gray
            pointer[i + 2] = gray
        }
    }
}
```

### 5. Consuming the Module in React Native

```tsx
import React, { useRef, useCallback } from 'react';
import { StyleSheet, View, Text, Button } from 'react-native';
import { NitroModules } from 'react-native-nitro-modules';
import type { ImageProcessorSpec } from './ImageProcessor.nitro';

// Synchronously resolve the hybrid native object
const ImageProcessor = NitroModules.createHybridObject<ImageProcessorSpec>('ImageProcessor');

export const FrameProcessingScreen: React.FC = () => {
  // Pre-allocate an RGBA frame buffer (1920 * 1080 * 4 bytes = ~8.29 MB)
  const bufferRef = useRef<ArrayBuffer>(new ArrayBuffer(1920 * 1080 * 4));

  const handleProcessFrame = useCallback(() => {
    ImageProcessor.configureFilter('grayscale', 1.0);

    // Synchronous, direct pointer execution without intermediate serialization
    ImageProcessor.processFrameBuffer(bufferRef.current, 1920, 1080);
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.title}>High-Throughput Buffer Processing</Text>
      <Button title="Process 1080p Frame" onPress={handleProcessFrame} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  title: { fontSize: 18, fontWeight: 'bold', marginBottom: 16 },
});
```

---

## Architectural Decision Matrix: TurboModules vs. Nitro Modules

Selecting the optimal native module architecture depends on execution frequency, data volume, and the underlying platform language requirements. Nitro Modules provide significant performance and ergonomics benefits for real-time streams, audio DSP, computer vision, and on-device ML inference. Conversely, standard TurboModules remain the conventional choice for general platform APIs, UI component bridges, and modules intended for upstream React Native Core integration.

```
                                  [Native Module Requirement]
                                               │
                        Does the module process continuous streams,
                       raw binary buffers, or 60+ Hz computations?
                                      /          \
                                   [YES]         [NO]
                                    /              \
                     Is direct Swift 5.9+ or     Are you wrapping standard
                     pure C++ core preferred?    platform OS SDKs / UI hooks?
                                  /                  \
                          [Nitro Modules]         [TurboModules]
```

### Choose Nitro Modules When:
1. **Building Real-Time Multimedia Pipelines:** Applications requiring real-time audio DSP, custom WebRTC transforms, computer vision frame processing (such as VisionCamera frame processors), or custom animation engines.
2. **Integrating On-Device AI / Embedded Engines:** Modules interacting with local inference engines (ONNX, Llama.cpp), embedded databases (MMKV, SQLite, Vector search), or custom cryptographic libraries.
3. **Targeting Pure C++ Cross-Platform Cores:** Projects sharing a single C++ codebase across iOS, Android, macOS, and Windows with minimal platform-specific glue code.
4. **Leveraging Modern Swift Ergonomics:** Writing modern Swift without `@objc` dynamic dispatch constraints or Objective-C bridging headers.

### Choose TurboModules When:
1. **Building Core React Native Infrastructure:** Modules intended for submission to the React Native core repository or standard community distributions adhering strictly to Meta's baseline tooling.
2. **Accessing Standard Platform APIs:** Infrequently invoked platform APIs (e.g., Battery Status, Biometric Authentication, Permissions, System Settings) where execution latency is non-critical.
3. **Maintaining Existing Java/Kotlin Native Modules:** Incremental migrations of legacy codebases that rely heavily on `ReactApplicationContext` lifecycles and established Android ecosystem wrappers.

---

## Technical FAQ and Integration Edge Cases

Integrating modern native module frameworks into enterprise codebases requires addressing practical questions about runtime coexistence, ABI boundaries, and platform support. Both Nitro Modules and TurboModules operate concurrently over the same JSI runtime without conflict. The following technical breakdown addresses common architectural questions regarding Swift interoperability, JNI usage, and thread safety.

### Can Nitro Modules and TurboModules coexist in the same application?
**Yes.** Both Nitro Modules and TurboModules build upon the same underlying JSI foundation and interact with the identical JavaScript runtime (Hermes/V8). An application can use standard TurboModules for platform services (such as Permissions or In-App Purchases) while employing Nitro Modules for compute-heavy subsystems (such as local vector search or camera processing).

### How does Swift-C++ interop in Nitro Modules differ from standard TurboModule Swift bridging?
Standard TurboModules require Swift classes to expose interfaces to Objective-C++ via `@objc` declarations and `NSObject` inheritance, routing calls through dynamic message dispatch (`objc_msgSend`). Nitro Modules utilize the native Swift 5.9+ C++ interoperability layer. Nitrogen generates standard C++ declarations that Swift imports directly, enabling inlined function calls and direct struct passing without Objective-C runtime overhead.

### Does Nitro Modules eliminate JNI usage on Android completely?
For modules written in pure C++, JNI is bypassed entirely because JavaScript communicates directly with C++ via JSI pointers. When a module interfaces with Kotlin or Java, JNI is still utilized to cross into the JVM, but Nitro minimizes overhead by leveraging statically generated C++/JNI bindings and direct memory buffers (`java.nio.ByteBuffer`).

---

### About the Author
**Furkan Çetinkaya** is a Mobile-focused Software Developer specializing in React Native, native bridge integrations (Kotlin & Swift), and supporting backend services. Experienced in maintaining high-impact mobile applications and developer SDKs.
- [GitHub](https://github.com/cetfu)
- [LinkedIn](https://www.linkedin.com/in/cetfu)