Turbo Modules in React Native enhance JavaScript interactions with native code, offering faster, more efficient communication than the traditional bridge model. By leveraging Turbo Modules, developers can seamlessly integrate performance-critical native features directly into their apps, ensuring high speed without relying on external libraries. This guide will show you how to build a native in-app module with Turbo Modules, optimizing your React Native app for a smoother, faster user experience.
In this guide, we will create a basic in-app native module that enables app restarts, utilizing Java for Android and Objective-C++ for iOS.
Step 1: Create spec file
Start by creating folder name specs at the root level. Inside the specs folder create a fileNamed NativeRestartPackage.ts
.
Note that the TurboModules requires the prefix Native to work.
Add the following code
import { TurboModule, TurboModuleRegistry } from "react-native";
export interface Spec extends TurboModule {
restartApp(): void;
}
export default TurboModuleRegistry.get<Spec>("NativeRestartApp") as Spec | null;
Step 2: Codgen Configuration
Add the below configuration to your package.json
"codegenConfig": {
"name": "NativeRestartPackageSpec",
"type": "modules",
"jsSrcsDir": "specs",
"android": {
"javaPackageName": "com.restartApp"
}
}
Step 3: Native Android Code
Create a folder named restartApp at android/app/src/main/java/com
.
Create NativeRestartApp.java
and add the code with all your native logic.
package com.restartApp;
import android.content.Context;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.jakewharton.processphoenix.ProcessPhoenix;
import com.restartApp.NativeRestartPackageSpec;
import android.util.Log;
public class NativeRestartApp extends NativeRestartPackageSpec{
private final Context mContext;
public static final String NAME = "NativeRestartApp";
public NativeRestartApp(ReactApplicationContext reactContext) {
super(reactContext);
this.mContext= reactContext.getApplicationContext();
}
@Override
public String getName() {
return NAME;
}
public void restartApp(){
Log.d("NativeRestartApp", "Restarting app");
ProcessPhoenix.triggerRebirth(mContext);
}
}
Create NativeRestartPackage.java
and add the following code.
package com.restartApp;
import com.facebook.react.TurboReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.module.model.ReactModuleInfo;
import com.facebook.react.module.model.ReactModuleInfoProvider;
import java.util.HashMap;
import java.util.Map;
public class NativeRestartPackage extends TurboReactPackage {
@Override
public NativeModule getModule(String name, ReactApplicationContext reactContext){
if (name.equals(NativeRestartApp.NAME)) {
return new NativeRestartApp(reactContext);
} else {
return null;
}
}
@Override
public ReactModuleInfoProvider getReactModuleInfoProvider() {
return new ReactModuleInfoProvider() {
@Override
public Map<String, ReactModuleInfo> getReactModuleInfos() {
Map<String, ReactModuleInfo> moduleInfoMap = new HashMap<>();
moduleInfoMap.put(
NativeRestartApp.NAME,
new ReactModuleInfo(
NativeRestartApp.NAME,
NativeRestartApp.NAME,
false, // canOverrideExistingModule
false, // needsEagerInit
false, // isCxxModule
true // isTurboModule
)
);
return moduleInfoMap;
}
};
}
}
Now its time to add the code into MainApplication
(Since this is an in-app-module and wont be autolinked).
import com.restartApp.NativeRestartPackage
//Rest of code
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(NativeRestartPackage())
}
//Rest of code
Step 4: Native iOS Code
Create a new group(using Xcode) in your app named NativeRestartApp.
Create RCTRestartApp.h
#import <Foundation/Foundation.h>
#import <NativeRestartPackageSpec/NativeRestartPackageSpec.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTRootView.h>
#import <React/RCTReloadCommand.h>
NS_ASSUME_NONNULL_BEGIN
@interface RCTRestartApp : NSObject<NativeRestartPackageSpec>
@end
NS_ASSUME_NONNULL_END
Create RCTRestartApp.mm
and add all your native logic here
#import "RCTRestartApp.h"
@implementation RCTRestartApp
RCT_EXPORT_MODULE(NativeRestartApp)
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
return std::make_shared<facebook::react::NativeRestartPackageSpecJSI>(params);
}
- (void)restartApp{
void (^loadBundle)(void) = ^{
RCTTriggerReloadCommandListeners(@"restart");
};
if ([NSThread isMainThread]) {
loadBundle();
} else {
dispatch_sync(dispatch_get_main_queue(), loadBundle);
}
}
@end
Step 5: Finally the Javascript code
import React from 'react';
import type { PropsWithChildren } from 'react';
import {
Button,
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';
import {
Colors,
DebugInstructions,
Header,
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen'
import NativeRestartPackage from './specs/NativeRestartPackage'
type SectionProps = PropsWithChildren<{
title: string;
}>;
function Section({ children, title }: SectionProps): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
return (
<View style={styles.sectionContainer}>
</View>
);
}
function App(): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};
return (
<SafeAreaView style={backgroundStyle}>
<StatusBar
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor={backgroundStyle.backgroundColor}
/>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
<Header />
<View
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
<Button title="Click me" onPress={() => {
NativeRestartPackage.restartApp()
}} />
{/* <LearnMoreLinks /> */}
</View>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
},
highlight: {
fontWeight: '700',
},
});
export default App;
Finally Run the app . You should be able to access native functionality bridgeless
Eg Video:-
https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lbbdyisp25wkq3kbpxow.gif
Conclusion
There is no conclusion , it just works like a charm .
Do like(at least for the cover meme XD)