Table of Contents
Building High-Performance Native Modules for React Native with Nitro


Book a call
React Native provides a powerful way to build cross-platform mobile apps using JavaScript and React. However, sometimes, developers need to interact with native platform features that aren't available in React Native's core API. This is where native modules come in.
Over the years, React Native has evolved with multiple ways to create native modules:
- Legacy Native Modules (traditional bridging approach)
- Turbo Modules (JSI-powered approach for better performance)
- Expo Modules (simplified native module creation within the Expo ecosystem)
- Nitro (a new approach that aims to improve native module development further)
In this article, we will explore Nitro, its benefits, and how it compares to other approaches. We'll also walk through an example of building a native module using Nitro.
Understanding React Native Native Modules
A native module in React Native allows JavaScript code to interact with platform-specific functionality written in Objective-C/Swift for iOS and Java/Kotlin for Android. These modules expose native functionality (like accessing device storage, sensors, or system utilities) to JavaScript.
With different approaches available, developers must decide which one best fits their project based on performance, ease of implementation, and ecosystem support.
Introduction to Nitro
Nitro is an emerging solution aimed at simplifying native module development by reducing boilerplate and improving performance. It builds on the foundation of Turbo Modules and JSI but focuses on improving developer experience and reducing boilerplate code.
How Nitro Works
Nitro is built on top of the JavaScript Interface (JSI), enabling direct communication between JavaScript and native code without relying on the traditional React Native bridge. This allows for synchronous execution, significantly improving performance compared to older methods.
The framework consists of three main components:
- Nitro Module: A library built with Nitro containing one or more Hybrid Objects
- Hybrid Object: A native object implemented in C++, Swift, or Kotlin that can be accessed directly from JavaScript
- Nitrogen: A code generator that creates native bindings from TypeScript interfaces, ensuring perfect type alignment between JavaScript and native code
Why Choose Nitro?
1. Exceptional Performance
Performance is where Nitro truly shines. According to benchmarks comparing method calls across different frameworks:
Test Case | ExpoModules | TurboModules | NitroModules |
100,000× addNumbers() | 434.85ms | 115.86ms | 7.27ms |
100,000× addStrings() | 429.53ms | 179.02ms | 29.94ms |
Nitro achieves this performance through:
- Lightweight Layer: While built on JSI, Nitro maintains efficiency with compile-time type-checking using C++ templates or constexpr, introducing zero runtime overhead
- Direct Swift/C++ Interop: Unlike other frameworks, Nitro doesn't rely on Objective-C at all, using Swift's C++ interop capabilities for near-zero overhead
- jsi::NativeState: Hybrid Objects are built on jsi::NativeState rather than jsi::HostObject, allowing for proper native prototypes and memory management
2. Strong Type Safety
Nitro Modules are both type-safe and null-safe. The code generator (Nitrogen) uses TypeScript specs as the single source of truth, with native interfaces generated to exactly match the declared types:
If you attempt to implement a function with incorrect return types, the app will not compile. This provides compile-time guarantees that prevent runtime type errors.
3. Object-Oriented Approach
Every Hybrid Object in Nitro is a native object that can be created, passed around, and destroyed. This allows for a true object-oriented programming model:
Functions are also first-class citizens in Nitro, meaning they can be kept in memory, called as needed, and automatically cleaned up when no longer needed.
Creating a Nitro Module
Let's walk through the process of creating a Nitro Module:
1. Initialize the Template
Start by creating a new Nitro Module using the CLI:
2. Define Your TypeScript Interface
Create a TypeScript interface that extends HybridObject:
3. Generate Native Interfaces
Run Nitrogen to generate the native interface specifications:
4. Implement Native Classes
Implement the generated specifications in Swift and/or Kotlin:
5. Register Your Hybrid Objects
Configure the nitro.json file to autolink your Hybrid Objects:
6. Use in JavaScript
Now you can use your Hybrid Object in JavaScript:
View Components with Nitro
Nitro also supports creating React Native Views, rendered via Fabric and backed by a C++ ShadowNode. Nitro Views use a more efficient prop parsing system than standard Fabric views.
Note: Nitro Views require react-native 0.78.0 or higher with the new architecture enabled.
Creating a Nitro View
Here's a simplified workflow for creating a Nitro View:
- Declare your view's interface:
- Generate code with npx nitro-codegen
- Implement the native view:
- Configure in nitro.json and run Nitrogen again
- Initialize in JavaScript:
- Use in your components:
Key Features
- Rich Types: Use any Nitro-supported type as props, including other Hybrid Objects
- Callbacks: Functions must be wrapped in an object: onEvent={{ f: (data) => console.log(data) }}
- Methods: Access methods through refs:
Comparing Native Module Solutions
Now let's compare Nitro with other frameworks for building native modules in React Native.
Turbo Modules
Turbo Modules are React Native's default framework for building native modules, using a code-generator called "codegen" to convert Flow/TypeScript specs to native interfaces.
Advantages of Turbo Modules:
- Shipped with React Native core: Unlike Nitro, Turbo Modules are part of React Native core. Users don't need to install any additional dependencies to build or use them.
- Part of the official React Native architecture
Disadvantages compared to Nitro:
No direct Swift support: Turbo Modules must go through Objective-C to reach Swift, creating an additional bridge layer:


- No properties (must use getter/setter methods)
- Not object-oriented (modules are singletons)
- No tuples or callbacks with return values
- Uses less efficient jsi::HostObject instead of jsi::NativeState
- Codegen runs on app build (not package build)
- Codegen cannot resolve imports
Legacy Native Modules
Before Turbo Modules, React Native provided "Native Modules" that used a communication layer with JSON messages instead of JSI.
Disadvantages compared to Nitro:
- Much slower due to serialization/deserialization
- Deprecated in favor of Turbo Modules
- All the same limitations as Turbo Modules
Expo Modules
Expo Modules provide an easy-to-use API for building native modules using a declarative syntax.
Advantages of Expo Modules:
- Swift and Kotlin support: Like Nitro, Expo Modules can be written in Swift instead of Objective-C
- Kotlin coroutines for async functions
- Property support
Disadvantages compared to Nitro:
Indirect Swift bridge: While Expo Modules support Swift, they still bridge through Objective-C, creating an additional layer:
- No code generator (less type safety)
- No tuples or callbacks with return values
- Uses less efficient jsi::HostObject
Type Support Comparison
Here's a comparison of supported types across the three frameworks:
JS Type | Expo Modules | Turbo Modules | Nitro Modules |
number, boolean, string | ✅ | ✅ | ✅ |
bigint | ✅ | ❌ | ✅ |
object, nullable types, arrays | ✅ | ✅ | ✅ |
Promise<T> | ✅ | ✅ | ✅ |
(T...) => void (callbacks) | ✅ | ✅ | ✅ |
(T...) => R (callbacks with return values) | ❌ | ❌ | ✅ |
[A, B, C, ...] (tuples) | ❌ | ❌ | ✅ |
A | B | C | ... (unions) | ✅ | ❌ | ✅ |
Record<string, T> | ❌ | ❌ | ✅ |
ArrayBuffer | ✅ | ❌ | ✅ |
Hybrid Objects | ✅ | ❌ | ✅ |
Custom interfaces | ❌ | ✅ | ✅ |
Enums | ❌ | ✅ | ✅ |
Conclusion
Nitro provides a powerful framework for building high-performance native modules for React Native. With its focus on performance, type safety, and modern language support, it offers significant advantages over other frameworks.
Key takeaways:
- Nitro is significantly faster than alternatives
- It provides strong type safety and null safety
- It supports a true object-oriented programming model
- It embraces modern languages (Swift, Kotlin) without legacy bridges
- It offers comprehensive type support
When building performance-critical features in React Native, Nitro should be high on your list of considerations. The development experience is excellent, and the runtime performance is unmatched among current native module solutions.
Whether you're building a complex image processing library, a high-performance AR experience, or just need to bridge efficiently to native SDKs, Nitro provides the tools to build robust, type-safe, and lightning-fast native modules.
Dive deep into our research and insights. In our articles and blogs, we explore topics on design, how it relates to development, and impact of various trends to businesses.