Nitro Modules w React Native 2026: Type-Safe Natywne Moduły Nowej Generacji

Nitro Modules to type-safe framework JSI dla React Native. Poznaj Nitrogen, model HybridObject, konfigurację Expo oraz benchmarki wydajności vs Turbo Modules.

Zaktualizowano: 22 sierpnia 2026

Nitro Modules to framework do budowy natywnych modułów React Native oparty na JSI, w którym schemat TypeScript jest źródłem prawdy. Generator Nitrogen produkuje z niego wiązania C++, Swift i Kotlin, dzięki czemu bridge między JS a natywem jest w pełni type-safe i wielokrotnie szybszy od Turbo Modules. W 2026 roku Nitro (wersja 0.30+) stał się domyślnym wyborem dla nowych bibliotek wymagających wysokiej wydajności, od react-native-mmkv po react-native-nitro-image. Pokażę tu architekturę Nitro, różnice względem Turbo Modules, konfigurację w Expo i bare workflow oraz praktyczny przykład HybridObject krok po kroku.

  • Nitro Modules wykorzystują JSI bez warstwy CodeGen z JSON schema. Nitrogen generuje wiązania natywne bezpośrednio z definicji TypeScript i interfejsów C++, eliminując dryf typów.
  • Benchmarki Margelo pokazują 15–30× szybszy narzut wywołań w porównaniu do Turbo Modules i ponad 100× względem starego Bridge dla operacji zwracających prymitywy.
  • Model HybridObject pozwala trzymać stan natywny bez ręcznego zarządzania referencjami. Obiekty są garbage-collectable po stronie JS, a Nitro synchronizuje cykl życia.
  • Nitrogen (npm: nitro-codegen) generuje kod przy każdym pod install / ./gradlew. Nie commitujesz wygenerowanych plików do repo.
  • Nitro działa z Expo przez config plugin oraz w bare React Native 0.75+ z włączoną Nową Architekturą (Fabric + Bridgeless).
  • Do niestandardowych typów (np. ArrayBuffer, Promise, callbacki) Nitro używa specjalizowanych konwerterów. Nie ma serializacji do JSON, dane przechodzą przez pamięć współdzieloną.

Czym są Nitro Modules?

Nitro Modules to framework opracowany przez Margelo (autorstwa Marca Rousavy'ego, twórcy react-native-mmkv i react-native-vision-camera), który stanowi alternatywę dla oficjalnego mechanizmu Turbo Modules w React Native. Powstał z jednego, konkretnego problemu: pisanie wysokowydajnych bibliotek natywnych w React Native pozostawało ćwiczeniem w duplikacji. Trzeba było ręcznie utrzymywać spec TypeScriptowy, wygenerowany kod CodeGen, wrapper C++ oraz implementacje Swift/Kotlin. Każda zmiana sygnatury metody wymagała synchronizacji czterech miejsc, a błąd typu wychodził dopiero na urządzeniu.

Szczerze mówiąc, sam się o to potknąłem w poprzednim projekcie, gdy migrowaliśmy moduł biometrii. Nitro odwraca ten model: definiujesz interfejs raz w TypeScript, opcjonalnie w nagłówku C++, a generator Nitrogen produkuje wszystkie warstwy pośrednie. Runtime Nitro nie używa mostu (bridge), nie serializuje do JSON i nie przechodzi przez CodeGen jsonschema. Komunikuje się bezpośrednio przez JSI, wykorzystując jsi::HostObject i wskaźniki C++ do obiektów natywnych. Efekt? Latencja pojedynczego wywołania spada z około 30–50 mikrosekund (Turbo Modules) do 1–3 mikrosekund (Nitro) dla synchronicznych metod zwracających prymitywy.

W 2026 roku ekosystem Nitro liczy już ponad 40 gotowych bibliotek, w tym react-native-mmkv v3, react-native-nitro-image, react-native-graphics-context oraz portów starszych modułów (np. react-native-nitro-sqlite). Wersja Nitro 0.30 dodała wsparcie dla iOS macros (Swift 5.9+) i Kotlin Multiplatform, co jeszcze bardziej upraszcza pisanie kodu natywnego.

Nitro Modules vs Turbo Modules: porównanie

Turbo Modules to oficjalny mechanizm Meta wprowadzony w ramach Nowej Architektury (patrz mój przewodnik migracji na React Native 0.80 i Nową Architekturę). Nitro nie zastępuje Turbo Modules. Obie technologie współistnieją w tym samym projekcie i można używać ich obok siebie. Różnice sprowadzają się głównie do sposobu generacji kodu, typów zwracanych i narzutu wywołań.

CechaNitro Modules 0.30Turbo Modules (RN 0.80)
Źródło prawdy typówTypeScript + C++ headerFlow / TypeScript spec + JSON schema
Generator koduNitrogen (osobne CLI)CodeGen (wbudowany w react-native)
Kanał komunikacjiJSI + HostObject + shared memoryJSI + JSON marshalling dla złożonych typów
Narzut wywołania (prymitywy)1–3 μs30–50 μs
Stan natywny per instancjaNatywnie przez HybridObjectRęczne zarządzanie referencjami
Wsparcie Swift/KotlinPierwszorzędne, generuje protokołyPierwszorzędne, generuje interfejsy
Krzywa uczeniaŚrednia, wymaga C++ dla shared codeWyższa, wymaga zrozumienia CodeGen
Dojrzałość ekosystemu (2026)40+ bibliotek, stabilne APISetki bibliotek, standard Meta

W praktyce: jeśli piszesz bibliotekę wymagającą tysięcy wywołań na sekundę (kamera, audio, kryptografia, storage), Nitro daje mierzalną przewagę. Dla modułów wywoływanych sporadycznie (np. udostępnienie natywnego ekranu ustawień) Turbo Modules wystarczą i nie wymagają dodatkowej zależności.

Instalacja i konfiguracja w projekcie

Nitro wymaga React Native 0.75+ z włączoną Nową Architekturą (Fabric + Bridgeless mode). Zacznij od instalacji dwóch pakietów: react-native-nitro-modules jako runtime oraz nitro-codegen jako devDependency. Ten drugi jest potrzebny tylko przy tworzeniu własnych modułów, konsumenci gotowych bibliotek instalują tylko runtime.

# Bare React Native lub Expo prebuild
npm install react-native-nitro-modules
npm install --save-dev nitro-codegen

# iOS: reinstalacja podów po dodaniu Nitro
cd ios && bundle exec pod install && cd ..

# Android: pierwsze wywołanie Nitrogena buduje shared library
npx nitro-codegen

W package.json dodaj skrypt uruchamiający Nitrogena przed buildem, aby wygenerowany kod zawsze był świeży:

{
  "scripts": {
    "postinstall": "nitro-codegen",
    "prebuild": "nitro-codegen",
    "specs": "nitro-codegen --logLevel debug"
  }
}

Wygenerowane pliki trafiają domyślnie do katalogu nitrogen/generated/. Nie commituj tego katalogu do repo. Dodaj go do .gitignore. To identyczna zasada jak w przypadku CodeGena Meta: kod jest regenerowalny i zależy od wersji Nitro.

Tworzenie pierwszego Nitro Module krok po kroku

Ok, do rzeczy. Zbudujemy prosty moduł Math, który wystawia metodę add i asynchroniczną hashFile. Zaczynamy od specyfikacji TypeScript, bo to jest źródło prawdy dla Nitrogena:

// src/specs/Math.nitro.ts
import type { HybridObject } from 'react-native-nitro-modules'

export interface Math extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
  // Synchroniczne, zero narzutu
  add(a: number, b: number): number

  // Asynchroniczne, zwraca natywnego Promise
  hashFile(path: string): Promise<string>

  // Property z gettera/settera
  readonly precision: number
}

Konwencja nazewnicza *.nitro.ts jest wymagana, Nitrogen skanuje pliki po tym sufiksie. Generyk HybridObject<{ ios, android }> deklaruje, w jakim języku będzie implementacja per platforma. Wspierane wartości: swift, kotlin, c++ (ten ostatni dla shared implementation).

Utwórz plik konfiguracyjny nitro.json w katalogu głównym pakietu:

{
  "cxxNamespace": ["math"],
  "ios": { "iosModuleName": "NitroMath" },
  "android": {
    "androidNamespace": ["math"],
    "androidCxxLibName": "NitroMath"
  },
  "autolinking": { "HybridMath": { "swift": "HybridMath", "kotlin": "HybridMath" } }
}

Uruchom generator: npx nitro-codegen. Nitrogen utworzy w nitrogen/generated/ kompletne szkielety, czyli protokół Swift HybridMathSpec, klasę Kotlin HybridMathSpec oraz nagłówki C++. Twoim zadaniem jest zaimplementowanie tylko konkretnej klasy:

// ios/HybridMath.swift
import NitroModules

final class HybridMath: HybridMathSpec {
  var precision: Double { 1e-9 }

  func add(a: Double, b: Double) throws -> Double {
    return a + b
  }

  func hashFile(path: String) throws -> Promise<String> {
    return Promise.async {
      let data = try Data(contentsOf: URL(fileURLWithPath: path))
      let digest = SHA256.hash(data: data)
      return digest.map { String(format: "%02x", $0) }.joined()
    }
  }
}
// android/src/main/java/com/margelo/math/HybridMath.kt
package com.margelo.math

import com.margelo.nitro.core.Promise
import java.io.File
import java.security.MessageDigest

class HybridMath : HybridMathSpec() {
  override val precision: Double = 1e-9

  override fun add(a: Double, b: Double): Double = a + b

  override fun hashFile(path: String): Promise<String> = Promise.async {
    val bytes = File(path).readBytes()
    MessageDigest.getInstance("SHA-256").digest(bytes)
      .joinToString("") { "%02x".format(it) }
  }
}

Po stronie JavaScript wywołanie modułu jest w pełni type-safe, TypeScript wnioskuje sygnatury z pliku .nitro.ts:

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

const math = NitroModules.createHybridObject<Math>('Math')

console.log(math.add(2, 3)) // 5, synchronicznie
console.log(await math.hashFile('/tmp/file.bin'))

Nitrogen: generator kodu C++, Swift i Kotlin

Nitrogen (pakiet nitro-codegen) to CLI napisane w TypeScript, które parsuje deklaracje *.nitro.ts przy pomocy kompilatora TypeScriptu (TS API), buduje graf typów i emituje pliki dla każdej platformy. Kluczowa różnica względem CodeGena Meta: Nitrogen operuje na AST TypeScriptu, nie na osobnym JSON schema, więc obsługuje pełne typy TypeScriptu, w tym union types, dyskryminowane unie, typy generyczne, template literal types.

Typy pierwotne mapowane są jak w tabeli poniżej. Dla typów złożonych Nitro generuje struct C++ i odpowiadające data class/struct w Kotlinie i Swifcie:

// TypeScript
type UploadOptions = {
  url: string
  headers: Record<string, string>
  onProgress: (progress: number) => void
}

// Wygenerowany Kotlin
data class UploadOptions(
  val url: String,
  val headers: Map<String, String>,
  val onProgress: (progress: Double) -> Unit
)

// Wygenerowany Swift
struct UploadOptions {
  let url: String
  let headers: [String: String]
  let onProgress: (Double) -> Void
}

Nitrogen wspiera również ArrayBuffer. Dane przechodzą bez kopiowania, przez wskaźnik do pamięci współdzielonej między JS a natywem. To krytyczne dla bibliotek przetwarzających obraz lub audio, gdzie kopiowanie kilku megabajtów na wywołanie byłoby nie do przyjęcia z punktu widzenia optymalizacji cold startu i wydajności runtime.

HybridObject: model programowania i cykl życia

HybridObject to fundamentalna abstrakcja Nitro. Każda instancja modułu jest obiektem posiadającym tożsamość, stan i cykl życia. To odróżnia Nitro od Turbo Modules, w których moduł jest zwykle singletonem bez stanu instancji. W Nitro możesz tworzyć wiele niezależnych instancji tego samego modułu, każda z własną pamięcią natywną:

// Każde createHybridObject to nowa instancja natywna
const cameraA = NitroModules.createHybridObject<Camera>('Camera')
const cameraB = NitroModules.createHybridObject<Camera>('Camera')
cameraA.setResolution(1080)
cameraB.setResolution(720) // niezależny stan

Cykl życia obiektu natywnego jest sprzężony z GC JavaScriptu. Gdy referencja JS znika, HybridObject wpada w kolejkę finalizacji, a Nitro wywołuje destruktor natywny (destruktor C++, deinit Swift, finalize() Kotlin). Dzięki temu nie trzeba ręcznie zwalniać zasobów, pod warunkiem że natywna implementacja poprawnie porządkuje po sobie w destruktorze.

Model ten idealnie pasuje do obiektów RAII: połączenia baz danych, uchwytów do plików, sesji sieciowych. Dla porównania w Turbo Modules trzeba było utrzymywać Map<String, NativeObject> po stronie natywnej i eksponować createInstance() oraz destroy(handle) jako osobne metody, co zawsze prowadziło do wycieków, jeśli JS zapomniał zwolnić handle. Więcej o debugowaniu takich wycieków znajdziesz w moim przewodniku po React Native DevTools 2026 i profilerze pamięci Hermesa.

Wydajność: benchmarki Nitro vs Turbo Modules

Margelo utrzymuje otwartą suite benchmarków w repozytorium Nitro na GitHubie. Poniższe liczby pochodzą z pomiarów na iPhone 15 Pro (A17 Pro) i Pixel 8 Pro (Tensor G3), Hermes V1, React Native 0.80, tryb Release:

  • Wywołanie synchroniczne add(a, b): number: Nitro 1.8 μs, Turbo Modules 42 μs, stary Bridge 380 μs.
  • Property access obj.value: Nitro 0.4 μs, Turbo Modules brak natywnego wsparcia (musisz definiować getter jako metodę).
  • Transfer ArrayBuffer 4 MB: Nitro 0.3 ms (bez kopii), Turbo Modules 12 ms (base64 marshalling).
  • Callback (progress: number) => void wywołany 1000×: Nitro 4 ms, Turbo Modules 68 ms.

Skąd taka różnica? Turbo Modules dla każdego wywołania buduje folly::dynamic po stronie C++, serializuje argumenty, przechodzi przez warstwę NativeModule, a dla typów złożonych używa JSON. Nitro operuje bezpośrednio na jsi::Value, bez pośredników, bez alokacji dynamicznych struktur, ze wskaźnikami C++ zamiast handle'i tekstowych. W praktyce oznacza to, że biblioteka jak react-native-vision-camera może dostarczać 60 klatek na sekundę przez callback JS bez zauważalnego wpływu na UI thread. Coś, co w Turbo Modules wymagałoby przenoszenia obróbki do worklet runtime.

Czy Nitro Modules działa z Expo?

Tak, Nitro w pełni wspiera Expo, ale wymaga workflow prebuild (dawniej "bare workflow"). Zwykłe Expo Go nie może uruchamiać niestandardowych modułów natywnych, niezależnie od tego, czy są Turbo, czy Nitro. Konfiguracja w projekcie Expo sprowadza się do dwóch kroków: instalacji pakietu i uruchomienia expo prebuild, które regeneruje katalogi ios/ i android/ z uwzględnieniem Nitro.

# Projekt Expo z Nitro
npx create-expo-app@latest my-app
cd my-app
npm install react-native-nitro-modules
npm install --save-dev nitro-codegen

# Wygeneruj natywne projekty
npx expo prebuild --clean

# Uruchom (development client, nie Expo Go)
npx expo run:ios
npx expo run:android

Nitro dostarcza własny config plugin Expo, który automatycznie konfiguruje Podfile i build.gradle. W app.json nic nie musisz dodawać, plugin jest wykrywany przez autolinking Expo. Jeśli używasz EAS Build, żaden dodatkowy krok nie jest wymagany. nitro-codegen uruchomi się w hooku postinstall na maszynach buildowych.

Kiedy używać Nitro Modules, a kiedy Turbo Modules?

W codziennej pracy architekta stosuję prostą regułę decyzyjną. Wybierz Nitro Modules, gdy: piszesz nową bibliotekę wymagającą wysokiej wydajności (kamera, audio, storage, kryptografia), potrzebujesz stanu per instancja (połączenie DB, sesja), transferujesz duże bufory binarne (ArrayBuffer bez kopiowania), lub gdy Twój zespół już utrzymuje inne biblioteki Nitro i chcesz spójności technologicznej.

Wybierz Turbo Modules, gdy: integrujesz się z bibliotekami dostarczanymi tylko w tej technologii (np. wewnętrzne SDK, moduły Meta), moduł jest wywoływany rzadko (kilka razy w sesji), zespół nie zna C++/Swift/Kotlin na poziomie potrzebnym do własnych bindingów, lub gdy priorytetem jest zerowa dodatkowa zależność w projekcie.

Warto pamiętać, że oba mechanizmy współistnieją w tym samym projekcie bez konfliktów. Nitro rejestruje swoje HybridObjecty przez własny mechanizm autolinkingu, niezależny od CodeGena Meta. W dużych aplikacjach fintechowych, nad którymi pracuję, spotykam typowo mieszankę: Turbo Modules dla wewnętrznych SDK partnerów, Nitro dla warstwy performance-critical (skanowanie dokumentów, biometria, kryptografia).

Najczęściej zadawane pytania

Czy Nitro Modules zastąpi Turbo Modules w oficjalnym React Native?

Nie, Nitro to niezależny projekt Margelo, a nie inicjatywa Meta. Meta nadal rozwija Turbo Modules jako standardowy mechanizm platformy. Nitro pozostaje alternatywą dla bibliotek wymagających maksymalnej wydajności lub bogatszego modelu obiektowego. Obie technologie działają obok siebie w tym samym projekcie.

Czy Nitro Modules jest gotowy na produkcję w 2026 roku?

Tak. Wersja 0.30 wprowadzona w Q1 2026 jest stabilna, ma semantyczne wersjonowanie oraz jest używana produkcyjnie przez react-native-mmkv v3, react-native-nitro-image, react-native-vision-camera 5 oraz kilkadziesiąt komercyjnych aplikacji. API core (HybridObject, konwertery typów) uważane jest za stabilne, breaking changes ogłaszane są z minimum 3-miesięcznym wyprzedzeniem.

Czy muszę znać C++, żeby pisać Nitro Modules?

Nie w większości przypadków. Jeśli piszesz moduł z osobnymi implementacjami dla iOS (Swift) i Android (Kotlin), znajomość C++ nie jest potrzebna. Nitrogen generuje warstwę C++ automatycznie. Znajomość C++ jest wymagana tylko dla shared implementation (jedna klasa C++ używana na obu platformach), zwykle stosowanej w bibliotekach kryptograficznych lub audio.

Jak debugować Nitro Modules?

Nitro w pełni wspiera React Native DevTools (Chrome DevTools Protocol). Możesz ustawiać breakpointy w kodzie JS wywołującym HybridObjecty. Dla warstwy natywnej używaj Xcode debuggera (iOS) i Android Studio (Android), breakpointy w Swift/Kotlin działają normalnie. Wygenerowane pliki Nitrogena są symbolizowane, więc stack trace prowadzi do konkretnej metody.

Czy Nitro Modules działają z Hermesem V1 i Bridgeless Mode?

Tak, Nitro wymaga wręcz Bridgeless Mode i pełni wykorzystuje Hermes V1 (domyślny w React Native 0.80+). Runtime Nitro komunikuje się przez JSI, które jest natywną warstwą Hermesa. Nie ma potrzeby dodatkowej konfiguracji: jeśli masz włączoną Nową Architekturę, Nitro działa od razu.

Yelena Petrov
O Autorze Yelena Petrov

React Native architect at a fintech. Builds platform teams, type-safe bridges, and runs the upgrade playbook so others don't have to.