React Native Native Modules
React Native covers most features through JavaScript libraries. Some device abilities have no ready-made library: a vendor payment terminal, a custom Bluetooth gadget, or a specialized scanner. A native module fills that gap. It lets JavaScript call code written in Kotlin or Java for Android and Swift or Objective-C for iOS.
The Bridge Between Two Worlds
┌─────────────────────┐ ┌──────────────────────┐ │ JavaScript Layer │ │ Native Layer │ │ (your screens) │ │ (Kotlin / Swift) │ │ │ call │ │ │ Battery.getLevel() ─┼────────► │ reads system battery │ │ │ ◄────────┼─ returns 82 │ └─────────────────────┘ result └──────────────────────┘
JavaScript asks, native code works, and the answer travels back. Think of a translator between two people who speak different languages.
When a Native Module Makes Sense
- A needed platform feature has no existing library
- An existing native SDK from a vendor must run inside the app
- A heavy calculation runs faster in native code
- A company shares one native codebase across several apps
Search the library directory first. A maintained package often exists, and it saves weeks of work.
Project Requirement
Native modules need access to the android and ios folders. A basic Expo Go session cannot load custom native code. Use a development build, or generate the native folders with a prebuild command.
npx expo prebuild
Example: A Device Name Module on Android
The Kotlin class below exposes one method that returns the device model.
// android/app/src/main/java/com/myapp/DeviceInfoModule.kt
package com.myapp
import android.os.Build
import com.facebook.react.bridge.*
class DeviceInfoModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
override fun getName() = "DeviceInfo"
@ReactMethod
fun getModel(promise: Promise) {
try {
promise.resolve(Build.MODEL)
} catch (e: Exception) {
promise.reject("ERR_MODEL", e)
}
}
}
Register the Module
A package class lists the module so React Native can discover it.
class DeviceInfoPackage : ReactPackage {
override fun createNativeModules(ctx: ReactApplicationContext) =
listOf(DeviceInfoModule(ctx))
override fun createViewManagers(ctx: ReactApplicationContext) =
emptyList<ViewManager<*, *>>()
}
Add DeviceInfoPackage() to the package list inside MainApplication.
Call It from JavaScript
import { NativeModules } from 'react-native';
const { DeviceInfo } = NativeModules;
const showModel = async () => {
try {
const model = await DeviceInfo.getModel();
console.log('Model:', model);
} catch (e) {
console.log('Could not read model');
}
};
Promises carry results back to JavaScript. A method can also receive a callback or emit events for ongoing updates.
Three Ways Native Code Talks Back
Method Best For ────────────────────────────────────────── Promise A single result (async/await) Callback Older style single result Event emitter Repeated updates (sensor streams)
Native UI Components
A native module supplies logic. A native UI component supplies a visible element, such as a custom map or a chart drawn by platform code. Native UI components use a view manager class, and JavaScript displays the result like any other tag.
<CustomChartView style={{ width: 300, height: 200 }} data={values} />
The New Architecture
Recent React Native versions introduce TurboModules and Fabric. TurboModules load native code on demand and call it directly, with less overhead than the older bridge. Typed specifications describe each method, and a code generator creates the glue code. Existing modules continue to work through a compatibility layer, so projects migrate gradually.
Old Bridge New Architecture ────────────────── ───────────────────────── Messages queue up Direct calls Data converted to JSON Shared memory access All modules load at start Modules load when needed
Testing and Maintenance
- Test on real Android and iOS devices
- Handle errors in native code and return clear messages
- Keep method names identical on both platforms
- Document the module for the whole team
- Rebuild the app after every native change, since a JavaScript reload does not update native code
Summary
Native modules connect JavaScript to platform code when no library covers a need. Write the Kotlin or Swift class, register it, and call it through NativeModules using promises. Search for existing packages first, use development builds, and learn the New Architecture for faster, typed communication.
