React Native allows developers to build cross-platform mobile apps with a single JavaScript codebase. But sometimes, the built-in features of React Native may not be sufficient, especially when you need to access device-specific features like camera, GPS, sensors, or Bluetooth.
This is where Native Modules come in. Native modules enable you to bridge Java (for Android) or Swift/Objective-C (for iOS) code into React Native, giving you direct access to native device features.
In this guide, you’ll learn:
- What Native Modules are.
- Why you might need them.
- How to create custom native modules for both Android and iOS.
What Are Native Modules in React Native?
A Native Module in React Native is a bridge between JavaScript and native code (Java/Swift/Objective-C). React Native provides a way to call native methods from JavaScript, allowing you to access device features not natively supported by React Native.
When Do You Need a Native Module?
- Access to features like Bluetooth, Camera, Sensors, or Biometric Authentication.
- Using custom SDKs that have no official React Native library.
- When you need better performance for computationally expensive tasks (e.g., image processing).
How Do Native Modules Work?
- JavaScript Side: Call native methods using the NativeModules API.
- Native Side (Java/Swift): Write custom functions to interact with native APIs.
- Bridge: The React Native bridge handles communication between JavaScript and Native code.
Creating a Custom Native Module (Step-by-Step Guide)
We’ll create a simple custom native module for both Android and iOS. The module will expose a function to get the device name.
Part 1: Creating a Native Module for Android
Step 1: Create a New React Native Project
npx react-native init NativeModuleExample
cd NativeModuleExample
Step 2: Create a New Native Module in Android (Java)
- Navigate to
android/app/src/main/java/com/nativemoduleexample/
. - Create a new file called DeviceInfoModule.java.
DeviceInfoModule.java
package com.nativemoduleexample;
import android.os.Build;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
public class DeviceInfoModule extends ReactContextBaseJavaModule {
DeviceInfoModule(ReactApplicationContext context) {
super(context);
}
@Override
public String getName() {
return "DeviceInfo";
}
@ReactMethod
public void getDeviceName(Promise promise) {
try {
String deviceName = Build.MODEL; // Get the device model name
promise.resolve(deviceName);
} catch (Exception e) {
promise.reject("Error", e);
}
}
}
Explanation
- ReactContextBaseJavaModule: Base class for native modules.
- @ReactMethod: Exposes the method to React Native.
- Promise: Used to return data asynchronously (similar to
resolve/reject
in JavaScript).
Step 3: Register the Module in MainApplication.java
- Open MainApplication.java.
- Add your module to the list of packages.
MainApplication.java
package com.nativemoduleexample;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;
import com.nativemoduleexample.DeviceInfoPackage; // Import the package
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List getPackages() {
return Arrays.asList(
new MainReactPackage(),
new DeviceInfoPackage() // Register the package here
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
}
Step 4: Create the Package Class (DeviceInfoPackage.java)
- In the same directory as DeviceInfoModule.java, create DeviceInfoPackage.java.
DeviceInfoPackage.java
package com.nativemoduleexample;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import java.util.Arrays;
import java.util.List;
import com.nativemoduleexample.DeviceInfoPackage; // Import the package
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List getPackages() {
return Arrays.asList(
new MainReactPackage(),
new DeviceInfoPackage() // Register the package here
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
}
Step 5: Call Native Module from JavaScript
App.js
import React, { useEffect, useState } from 'react';
import { View, Text, NativeModules } from 'react-native';
const App = () => {
const [deviceName, setDeviceName] = useState('');
useEffect(() => {
NativeModules.DeviceInfo.getDeviceName()
.then((name) => setDeviceName(name))
.catch((error) => console.error(error));
}, []);
return (
Device Name: {deviceName}
);
};
export default App;
Part 2: Creating a Native Module for iOS (Swift)
Step 1: Open the iOS Project in Xcode
npx pod-install
Open the iOS project in Xcode.
Step 2: Create a New Native Module
- Right-click the NativeModuleExample folder and create a new Swift file called DeviceInfo.swift.
- Xcode will ask if you’d like to create a Bridging Header. Click Yes.
DeviceInfo.swift
import Foundation
@objc(DeviceInfo)
class DeviceInfo: NSObject {
@objc
func getDeviceName(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
let deviceName = UIDevice.current.name
resolve(deviceName)
}
@objc
static func requiresMainQueueSetup() -> Bool {
return false
}
}
Step 3: Register the Module in RCTBridgeModule
- Open AppDelegate.m and import the module.
- Create a bridging file DeviceInfo.m.
DeviceInfo.m
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(DeviceInfo, NSObject)
RCT_EXTERN_METHOD(getDeviceName:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
@end
Step 4: Call Native Module from JavaScript
App.js
import React, { useEffect, useState } from 'react';
import { View, Text, NativeModules } from 'react-native';
const App = () => {
const [deviceName, setDeviceName] = useState('');
useEffect(() => {
NativeModules.DeviceInfo.getDeviceName()
.then((name) => setDeviceName(name))
.catch((error) => console.error(error));
}, []);
return (
Device Name: {deviceName}
);
};
export default App;
Testing the Native Module
Run the following command to test the app on Android or iOS:
npx react-native run-android
npx react-native run-ios