Skip to main content

Command Palette

Search for a command to run...

Building High-Performance Native Modules for React Native with Nitro

Published
9 min read
Building High-Performance Native Modules for React Native with Nitro

Learn how Nitro revolutionizes React Native native modules with unmatched speed, type safety, and streamlined code. Boost app performance now.

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 — providing 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:

npx create-nitro-module@latest my-nitro-module

2. Define Your TypeScript Interface

Create a TypeScript interface that extends HybridObject:

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

export interface Math extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
  add(a: number, b: number): number
  fibonacci(n: number): number
}

3. Generate Native Interfaces

Run Nitrogen to generate the native interface specifications:

npx nitro-codegen

4. Implement Native Classes

Implement the generated specifications in Swift and/or Kotlin:

// iOS - HybridMath.swift
class HybridMath: HybridMathSpec {
  func add(a: Double, b: Double) throws -> Double {
    return a + b
  }

  func fibonacci(n: Double) throws -> Double {
    if n <= 1 { return n }
    return try fibonacci(n: n - 1) + fibonacci(n: n - 2)
  }
}
// Android - HybridMath.kt
class HybridMath: HybridMathSpec() {
  override fun add(a: Double, b: Double): Double = a + b

  override fun fibonacci(n: Double): Double {
    if (n <= 1.0) return n
    return fibonacci(n - 1) + fibonacci(n - 2)
  }
}

5. Register Your Hybrid Objects

Configure the nitro.json file to autolink your Hybrid Objects:

{
  "ios": {
    "classes": ["HybridMath"]
  },
  "android": {
    "classes": ["HybridMath"]
  }
}

6. Use in JavaScript

Now you can use your Hybrid Object in JavaScript:

import { NitroModules } from 'react-native-nitro-modules'
import type { Math } from './Math.nitro'

const math = NitroModules.createHybridObject<Math>('Math')
const result = math.add(3, 7)  // 10

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

Declare your view's interface:

import type { HybridView } from 'react-native-nitro-modules'

interface VideoPlayerProps {
  url: string
  isPlaying: boolean
  onProgress: { f: (progress: number) => void }
}

export interface VideoPlayer extends HybridView<VideoPlayerProps> {}

Generate code with npx nitro-codegen, then implement the native view, configure in nitro.json, and initialize in JavaScript:

import { NitroModules } from 'react-native-nitro-modules'
import type { VideoPlayer } from './VideoPlayer.nitro'

const VideoPlayerView = NitroModules.createNitroView<VideoPlayer>('VideoPlayer')

Use in your components:

<VideoPlayerView
  url="https://example.com/video.mp4"
  isPlaying={true}
  onProgress={{ f: (p) => console.log(p) }}
/>

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 — no additional dependencies needed
  • Part of the official React Native architecture

Disadvantages compared to Nitro:

Turbo Modules flow diagram

  • No direct Swift support — must go through Objective-C, 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 — can be written in Swift instead of Objective-C
  • Kotlin coroutines for async functions
  • Property support

Disadvantages compared to Nitro:

Turbo to Expo Module bridge diagram

  • Indirect Swift bridge — still bridges 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.


Originally published on GeekyAnts Blog


---
8 views