Introduction
React Native continues to evolve, and with version 0.79, bridging native code has become more efficient—especially when using Cross-Platform C++ Native Modules. Writing modules in C++ allows you to share platform-agnostic logic between Android and iOS with a single implementation, reducing duplication and improving performance.
In this guide, we'll walk through:
✅ Creating a C++ Turbo Native Module
✅ Configuring Codegen for scaffolding
✅ Implementing native logic once for both platforms
✅ Registering the module in Android & iOS
✅ Testing the module in JavaScript
💡 Why Use Cross-Platform C++?
Before diving into implementation, let’s understand why using C++ for native modules is a game changer:
✅ Single Source of Truth
With C++, you write the logic once and use it across both Android and iOS. No need to duplicate functionality in Kotlin and Swift/Objective-C—this greatly reduces development and maintenance effort.
🚀 Performance Optimized
C++ offers low-level system access and is highly optimized for performance. For CPU-intensive tasks such as encryption, string manipulation, and audio/video processing, C++ outperforms JavaScript and even many high-level native APIs.
🔄 Better Code Sharing
C++ modules can be reused not only between platforms but even across different apps or teams, especially if you’re building SDKs or libraries. This ensures consistency in logic across platforms.
🔒 Leverage Existing C++ Libraries
There’s a vast ecosystem of mature C++ libraries (e.g., OpenCV, SQLite, Boost). By writing C++ modules, you can tap into these without rewriting logic.
🧱 Tightly Integrated with React Native’s New Architecture
TurboModules and the JSI (JavaScript Interface) enable seamless and efficient communication between JS and C++. This means better memory management, faster calls, and lower overhead.
🚀 Step 1: Create the JavaScript Specs
Turbo Native Modules require a specification file (written in TypeScript) that defines the module's interface.
-
Create a
specs
folder in your project root. -
Add
NativeSampleModule.ts
with the following:
import { TurboModule, TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
reverseString(input: string): string;
}
export default TurboModuleRegistry.getEnforcing<Spec>('NativeSampleModule');
🔹 Key Notes:
- The file must start with
Native
(e.g.,NativeSampleModule.ts
). - The
Spec
interface defines the module's methods.
🚨 Important Note About Spec Files
When creating your spec file (NativeSampleModule.ts
), you must:
- Keep it as a TypeScript (.ts) file - Don't change it to JavaScript (.js) or Codegen won't process it
-
Always prefix with "Native" - The filename must start with
Native
(e.g.,NativeSampleModule.ts
), otherwise Codegen will ignore it
⚙️ Step 2: Configure Codegen
Update package.json
with the following configuration, making sure to replace com.yourpackagename
with your actual package name:
"codegenConfig": {
"name": "AppSpecs",
"type": "modules",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.yourpackagename.specs" // ← Replace with your package name!
}
}
This tells Codegen where to find the specs and how to generate scaffolding
💻 Step 3: Write the C++ Native Module
Create a shared
Folder
Place it at the same level as android
and ios
.
Define the Header (NativeSampleModule.h)
//shared/NativeSampleModule.h
#pragma once
#include <AppSpecsJSI.h>
#include <memory>
#include <string>
namespace facebook::react {
class NativeSampleModule : public NativeSampleModuleCxxSpec<NativeSampleModule> {
public:
NativeSampleModule(std::shared_ptr<CallInvoker> jsInvoker);
std::string reverseString(jsi::Runtime& rt, std::string input);
};
} // namespace facebook::react
Implement the Module (NativeSampleModule.cpp)
//shared/NativeSampleModule.cpp
#include "NativeSampleModule.h"
namespace facebook::react {
NativeSampleModule::NativeSampleModule(std::shared_ptr<CallInvoker> jsInvoker)
: NativeSampleModuleCxxSpec(std::move(jsInvoker)) {}
std::string NativeSampleModule::reverseString(jsi::Runtime& rt, std::string input) {
return std::string(input.rbegin(), input.rend());
}
} // namespace facebook::react
🔹 What’s Happening?
- The header declares the module structure.
- The implementation reverses a string (shared logic for both platforms).
🤖 Step 4: Register the Module in Android
1. Add CMakeLists.txt
Create android/app/src/main/jni/CMakeLists.txt:
cmake_minimum_required(VERSION 3.13)
# Define the library name here.
project(appmodules)
# This file includes all the necessary to let you build your React Native application
include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake)
# Define where the additional source code lives. We need to crawl back the jni, main, src, app, android folders
target_sources(${CMAKE_PROJECT_NAME} PRIVATE ../../../../../shared/NativeSampleModule.cpp)
# Define where CMake can find the additional header files. We need to crawl back the jni, main, src, app, android folders
target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC ../../../../../shared)
2. Update build.gradle
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://reactnative.dev/docs/signed-apk-android.
signingConfig signingConfigs.debug
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
+ externalNativeBuild {
+ cmake {
+ path "src/main/jni/CMakeLists.txt"
+ }
+ }
}
3. Register the new Turbo Native Module
The final step is to register the new C++ Turbo Native Module in the runtime, so that when JS requires the C++ Turbo Native Module, the app knows where to find it and can return it.
- From the folder
SampleApp/android/app/src/main/jni
, run the following command:
curl -O https://raw.githubusercontent.com/facebook/react-native/v0.76.0/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp
- Then, modify this file as follows:
android/app/src/main/jni/OnLoad.cpp
#include <DefaultComponentsRegistry.h>
#include <DefaultTurboModuleManagerDelegate.h>
#include <autolinking.h>
#include <fbjni/fbjni.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
#include <rncore.h>
+ // Include the NativeSampleModule header
+ #include <NativeSampleModule.h>
//...
std::shared_ptr<TurboModule> cxxModuleProvider(
const std::string& name,
const std::shared_ptr<CallInvoker>& jsInvoker) {
// Here you can provide your CXX Turbo Modules coming from
// either your application or from external libraries. The approach to follow
// is similar to the following (for a module called `NativeCxxModuleExample`):
//
// if (name == NativeCxxModuleExample::kModuleName) {
// return std::make_shared<NativeCxxModuleExample>(jsInvoker);
// }
+ // This code register the module so that when the JS side asks for it, the app can return it
+ if (name == NativeSampleModule::kModuleName) {
+ return std::make_shared<NativeSampleModule>(jsInvoker);
+ }
// And we fallback to the CXX module providers autolinked
return autolinking_cxxModuleProvider(name, jsInvoker);
}
// leave the rest of the file
🍏 Step 5: Register the Module in iOS
To make sure that the iOS app can effectively build the C++ Turbo Native Module, we need to:
- Install pods and run Codegen.
- Add the shared folder to our iOS project.
- Register the C++ Turbo Native Module in the application.
1. Install Pods and Run Codegen.
cd ios
bundle install
bundle exec pod install
2. Add the shared folder to the iOS project
This step adds the shared folder to the project to make it visible to Xcode.
1. Open the CocoPods generated Xcode Workspace.
cd ios
open SampleApp.xcworkspace
2. Click on the SampleApp
project on the left and select Add files to "Sample App"....
3. Select the shared
folder and click on Add.
If you did everything right, your project on the left should look like this:
3. Registering the Cxx Turbo Native Module in your app
To register a pure Cxx Turbo Native Module in your app, you need to:
- Create a ModuleProvider for the Native Module
- Configure the package.json to associate the JS module name with the ModuleProvider class.
The ModuleProvider is an Objective-C++ that glues together the Pure C++ module with the rest of your iOS App.
3.1 Create the ModuleProvider
- From Xcode, select the SampleApp project and press ⌘ + N to create a new file.
- Select the Cocoa Touch Class template
- Add the name SampleNativeModuleProvider (keep the other field as Subclass of: NSObject and Language: Objective-C)
- Click Next to generate the files.
- Rename the SampleNativeModuleProvider.m to SampleNativeModuleProvider.mm. The mm extension denotes an Objective-C++ file.
- Implement the content of the SampleNativeModuleProvider.h with the following:
NativeSampleModuleProvider.h
#import <Foundation/Foundation.h>
#import <ReactCommon/RCTTurboModule.h>
NS_ASSUME_NONNULL_BEGIN
@interface NativeSampleModuleProvider : NSObject <RCTModuleProvider>
@end
NS_ASSUME_NONNULL_END
This declares a NativeSampleModuleProvider object that conforms to the RCTModuleProvider protocol.
- Implement the content of the SampleNativeModuleProvider.mm with the following:
NativeSampleModuleProvider.mm
#import "NativeSampleModuleProvider.h"
#import <ReactCommon/CallInvoker.h>
#import <ReactCommon/TurboModule.h>
#import "NativeSampleModule.h"
@implementation NativeSampleModuleProvider
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return std::make_shared<facebook::react::NativeSampleModule>(params.jsInvoker);
}
@end
3.2 Update the package.json
Modify the package.json
as it follows:
"start": "react-native start",
"test": "jest"
},
"codegenConfig": {
"name": "AppSpecs",
"type": "modules",
"jsSrcsDir": "specs",
"android": {
"xxxx": "com.xxp.xx"
},
"ios": {
"modulesProvider": {
"NativeSampleModule": "NativeSampleModuleProvider"
}
}
},
"dependencies": {
At this point, you need to re-install the pods to make sure that codegen runs again to generate the new files:
🧪 Step 6: Test in JavaScript
Modify App.tsx:
import React from 'react';
import { Button, SafeAreaView, Text, TextInput, View } from 'react-native';
import SampleTurboModule from './specs/NativeSampleModule';
function App() {
const [value, setValue] = React.useState('');
const [reversedValue, setReversedValue] = React.useState('');
const onPress = () => {
const revString = SampleTurboModule.reverseString(value);
setReversedValue(revString);
};
return (
<SafeAreaView>
<Text>Enter text to reverse:</Text>
<TextInput value={value} onChangeText={setValue} />
<Button title="Reverse" onPress={onPress} />
<Text>Reversed: {reversedValue}</Text>
</SafeAreaView>
);
}
export default App;
🎉 Conclusion
You’ve successfully created a cross-platform C++ Turbo Native Module in React Native 0.79!
🔹 Benefits:
- ✔ Single codebase for Android & iOS
- ✔ Better performance with C++
- ✔ Type-safe with Codegen
Try extending this by adding more methods or integrating third-party C++ libraries!
🚀 Happy Coding! 🚀
Further Reading
Would you like a deep dive into advanced C++ module optimizations? Let me know in the comments! 👇
Top comments (0)