RN CLI App with local nitro module (as subpackages)
π Official Guide: using Nitro in your app via a subpackage
Prefer reading code? Skip this README and check out the code changes
A React Native project demonstrating the in-app integration of Nitro Modules with both iOS and Android platforms. This project shows how to create hybrid objects that can perform basic calculations from the native side, and showcases a structure.
β οΈ Important: The React Native project is named "NitroExample", which should not be confused with the iOS and Android Nitro module also named "NitroExample" (see nitro.json). The project name and the Nitro module name happen to be the same in this example, but they serve different purposes:
- Project name: The React Native app container
- Nitro module name: The native library that provides the math functionality
This project implements a MathCalculator
Nitro module that provides basic arithmetic operations (add, subtract, multiply, divide) using native iOS (Swift) and Android (Kotlin) implementations, bridged through Nitro's C++ core.
NitroExample/
βββ π± App.tsx # Main React Native app
βββ π§ nitro.json # Nitro configuration
βββ π nitrospecs/ # TypeScript Nitro specifications
β βββ MathCalculator.nitro.ts
βββ 𧬠nitrogen/generated/ # Auto-generated by nitrogen
β βββ shared/c++/ # Cross-platform C++ specs
β βββ ios/ # iOS-specific generated files
β β βββ HybridMathCalculator.swift # π iOS implementation
β β βββ NitroExample+autolinking.rb # iOS autolinking helper
β β βββ ... # Other generated iOS files
β βββ android/ # Android-specific generated files
β βββ NitroExample+autolinking.gradle # Android autolinking helper
β βββ NitroExample+autolinking.cmake # Android CMake autolinking
β βββ ... # Other generated Android files
βββ π¦ src/
β βββ components/
β β βββ MathCalculatorExample.tsx # Demo component using Nitro module
β βββ modules/ # JS/TS module definitions
β βββ MathCalculator.ts # TypeScript interface to Nitro module
βββ π NitroExample.podspec # iOS package definition (root)
βββ π€ nitro-example/ # Android Nitro module
β βββ build.gradle # Android library configuration
β βββ CMakeLists.txt # Android CMake setup
β βββ src/main/ # Android source code
β βββ AndroidManifest.xml
β βββ cpp/cpp-adapter.cpp # JNI bridge
β βββ java/com/margelo/nitro/example/
β βββ HybridMathCalculator.kt # π€ Android implementation
β βββ NitroExamplePackage.kt # React Native package
βββ π ios/ # iOS React Native app
β βββ Podfile # CocoaPods dependencies
β βββ ...
βββ π€ android/ # Android React Native app
βββ settings.gradle # Includes nitro-example module
βββ ...
- React Native development environment
- Install Nitro Modules:
yarn add react-native-nitro-modules
- Install Nitrogen codegen:
yarn add nitro-codegen --dev
- Create nitro config in
nitro.json
{
"$schema": "https://nitro.margelo.com/nitro.schema.json",
"cxxNamespace": ["example"],
"ios": {
"iosModuleName": "NitroExample"
},
"android": {
"androidNamespace": ["example"],
"androidCxxLibName": "NitroExample"
},
}
see nitrospecs/MathCalculator.nitro.ts
see nitro.json
When adding new Nitro specs:
- Create spec in
nitrospecs/YourSpec.nitro.ts
- Run codegen:
npx nitro-codegen
- Implement in platforms:
see
Creating subpackages
section- iOS: Create implementation in
nitrogen/generated/ios/HybridMathCalculator.swift
- Android: Create implementation in
nitro-example/src/main/java/.../HybridMathCalculator.kt
- Android: Create React Native package in
nitro-example/src/main/java/.../NitroExamplePackage.kt
- iOS: Create implementation in
- Register in
nitro.json
:
{
"autolinking": {
"YourSpec": {
"swift": "HybridYourSpec",
"kotlin": "HybridYourSpec"
}
}
}
For iOS, the setup uses autolinking via CocoaPods:
- Create the podspec in project root (
NitroExample.podspec
):
Pod::Spec.new do |s|
s.name = 'NitroExample'
s.version = '1.0.0'
s.summary = 'Native Math Implementation'
# ... other podspec config
# π₯ Use Nitrogen's autolinking
load 'nitrogen/generated/ios/NitroExample+autolinking.rb'
add_nitrogen_files(s)
end
-
Create iOS implementation in
nitrogen/generated/ios/HybridMathCalculator.swift
-
Add to Podfile in
ios/Podfile
:
target 'NitroExample' do
pod 'NitroExample', :path => '../'
# ... other dependencies
end
- Install pods:
yarn pods
For Android, the setup uses Nitrogen's autolinking via the local Android module:
-
Create local Android library in
nitro-example/
directory- This contains
build.gradle
,CMakeLists.txt
, andsrc/
with Android implementations
- This contains
-
Autolinking in build.gradle:
// Use Nitrogen's generated autolinking
apply from: '../nitrogen/generated/android/NitroExample+autolinking.gradle'
android {
namespace "com.margelo.nitro.example"
// ... rest of configuration
}
- Autolinking in CMakeLists.txt:
# Use Nitrogen's generated autolinking
include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/NitroExample+autolinking.cmake)
# Create library with your custom sources
add_library(
${PACKAGE_NAME}
SHARED
src/main/cpp/cpp-adapter.cpp
)
- Include in main app (
android/settings.gradle
):
include ':nitro-example'
project(':nitro-example').projectDir = new File(rootProject.projectDir, '../nitro-example')
- Add dependency (
android/app/build.gradle
):
dependencies {
implementation project(':nitro-example')
}
-
Create Android implementations:
- Kotlin implementation (
nitro-example/src/main/java/.../HybridMathCalculator.kt
):
@DoNotStrip class HybridMathCalculator : HybridMathCalculatorSpec() { override fun add(a: Double, b: Double): Double = a + b override fun subtract(a: Double, b: Double): Double = a - b override fun multiply(a: Double, b: Double): Double = a * b override fun divide(a: Double, b: Double): Double { if (b == 0.0) throw IllegalArgumentException("Division by zero") return a / b } }
- React Native package (
nitro-example/src/main/java/.../NitroExamplePackage.kt
):
class NitroExamplePackage : ReactPackage { init { // Initialize the native library using the generated loader NitroExampleOnLoad.initializeNative() } override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> = emptyList() override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> = emptyList() }
- Kotlin implementation (
-
Register package in MainApplication.kt:
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(NitroExamplePackage())
}
import { MathCalculator } from './src/modules/MathCalculator';
// Use the native math calculator
const result = MathCalculator.add(5, 3); // Returns: 8
const product = MathCalculator.multiply(4, 7); // Returns: 28
const quotient = MathCalculator.divide(10, 2); // Returns: 5
- Regenerate specs:
npx nitro-codegen
(after modifying.nitro.ts
files) - iOS build:
yarn pods && yarn ios
- Android build:
yarn android
- The
nitrogen/generated/
directory is auto-generated - don't modify these files - iOS implementation goes in
nitrogen/generated/ios/HybridMathCalculator.swift
because it needs to sit beside the generatedHybridMathCalculatorSpec
to work properly - Android implementation requires two files:
nitro-example/src/main/java/.../HybridMathCalculator.kt
- extendsHybridMathCalculatorSpec
nitro-example/src/main/java/.../NitroExamplePackage.kt
- React Native package for library initializationnitro-example/
sits in the project root because the autolinking files generated by nitrogen expect a specific relative path to find thenitrogen/
directory
- Both platforms share the same C++ specs from
nitrogen/generated/shared/
- One
nitro.json
per project: All Nitro specs must be defined in a single configuration file - Single module name: All generated code shares the same module name (
NitroExample
in this case) - Shared namespace: All specs are generated under the same C++ namespace and platform packages
- Unlimited specs: You can create as many
.nitro.ts
files as needed in thenitrospecs/
directory - Single module output: All specs are compiled into the same native module
- Shared autolinking: All specs use the same autolinking configuration
- One iOS podspec: Single
.podspec
file handles all Nitro specs for iOS - One Android module: Single
nitro-example/
module contains all Android implementations - Shared native library: All specs compile into one native library per platform
β What you CAN do:
- Create multiple Nitro specs (e.g.,
MathCalculator.nitro.ts
,StringUtils.nitro.ts
,ImageProcessor.nitro.ts
) - Implement all specs in the same platform modules
- Register multiple specs in the same
nitro.json
autolinking section
β What you CANNOT do:
- Have separate
nitro.json
files for different feature areas - Create multiple iOS podspecs for different Nitro modules
- Split Android implementations across multiple native modules
- Use different C++ namespaces or Android package names for different specs
- Create specs:
nitrospecs/
βββ MathCalculator.nitro.ts
βββ StringUtils.nitro.ts
βββ ImageProcessor.nitro.ts
- run
npx nitro-codegen
- Implement in platforms:
-
iOS: nitrogen/generated/ios/HybridMathCalculator.swift, HybridStringUtils.swift, etc.
-
Android: nitro-example/src/main/java/.../HybridMathCalculator.kt, HybridStringUtils.kt, etc.
- Register in nitro.json
{
"autolinking": {
"MathCalculator": {
"swift": "HybridMathCalculator",
"kotlin": "HybridMathCalculator"
},
"StringUtils": {
"swift": "HybridStringUtils",
"kotlin": "HybridStringUtils"
},
"ImageProcessor": {
"swift": "HybridImageProcessor",
"kotlin": "HybridImageProcessor"
}
}
}
- Add more modules in
src/modules
along with MathCalculator.ts
π‘ Both
NitroExamplePackage.kt
andNitroExample.podspec
remain completely unchanged when you add multiple specs
The beauty of Nitrogen's design is that the package/podspec setup is truly one-time - the autolinking system scales automatically! β¨