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.
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ń.
Cecha
Nitro Modules 0.30
Turbo Modules (RN 0.80)
Źródło prawdy typów
TypeScript + C++ header
Flow / TypeScript spec + JSON schema
Generator kodu
Nitrogen (osobne CLI)
CodeGen (wbudowany w react-native)
Kanał komunikacji
JSI + HostObject + shared memory
JSI + JSON marshalling dla złożonych typów
Narzut wywołania (prymitywy)
1–3 μs
30–50 μs
Stan natywny per instancja
Natywnie przez HybridObject
Ręczne zarządzanie referencjami
Wsparcie Swift/Kotlin
Pierwszorzędne, generuje protokoły
Pierwszorzędne, generuje interfejsy
Krzywa uczenia
Średnia, wymaga C++ dla shared code
Wyższa, wymaga zrozumienia CodeGen
Dojrzałość ekosystemu (2026)
40+ bibliotek, stabilne API
Setki 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:
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:
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:
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.
Praktyczny przewodnik po Maestro E2E dla React Native w 2026: instalacja w Expo, pierwszy scenariusz YAML, Maestro Studio, Maestro Cloud oraz integracja z EAS Build i GitHub Actions.
Kompletny przewodnik po skracaniu cold startu w React Native 2026. Pomiar TTI w Instruments i Perfetto, Hermes V1, Nowa Architektura z Bridgeless Mode oraz cięcie bundla poniżej 4 MB, z konkretnymi patchami z produkcji.