Learn React Native - Native Modules & TurboModules
Episode 10 of 23

Learn React Native - Native Modules & TurboModules

This episode covers the bridge between JavaScript and native: the difference between legacy bridge modules and TurboModules in the New Architecture, the interop layer, writing your first native module in Kotlin, calling it from JavaScript, and getting to know popular community modules.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

React Native provides access to native features through native modules. Without modules, you can't reach sensors, the system keyboard, or native libraries only available in the iOS and Android SDKs. This capability is what distinguishes a native app from a mere web app.

Episode 10 explains how modules work in the legacy and new architectures — bridge vs TurboModules — including the interop layer that keeps old libraries running. You'll also write your first native module in Kotlin and call it from JavaScript, then get to know popular community modules.

Legacy Bridge vs TurboModules

How Legacy Modules Work

In the legacy architecture, native modules are registered and then called through the bridge, which is asynchronous and serializes data to JSON. Every call carries overhead, and all modules are loaded at once at app startup — consuming time and memory.

TurboModules in the New Architecture

TurboModules replace this mechanism with JSI — direct JavaScript-to-native calls without JSON serialization. Modules are also loaded lazily: only loaded when first used. The combination speeds up startup and reduces memory usage.

Interop Layer for Legacy Libraries

Why Interop Is Needed

Tens of thousands of libraries on npm were written for the legacy architecture. If all of them had to be rewritten, the New Architecture migration would take years. That's why React Native provides an interop layer: legacy bridge modules are adapted automatically so they keep working on the new architecture without changing library code.

Consequences of Interop

Interop works, but its performance remains bridge-like — serialization and asynchrony. For the best performance, choose libraries that already use native JSI. When choosing a library, check its support status on reactnative.directory; episodes 21 and 17 will cover this in more depth.

Writing Your First Native Module in Kotlin

Creating the Android Module

In a CLI project, Android modules are written in Kotlin in the android/app/src/main/java folder. Here's a simple module that adds two numbers:

Native module in Kotlin
package com.myapp
 
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
 
class CalculatorModule : ReactContextBaseJavaModule() {
    override fun getName(): String = "Calculator"
 
    @ReactMethod
    fun tambah(a: Double, b: Double, promise: Promise) {
        promise.resolve(a + b)
    }
}

getName() returns the module name used from JavaScript, and @ReactMethod marks the callable function. Functions with a Promise allow async results to be sent back to JS.

Registering the Module

The module must be registered through a package for React Native to recognize it. Create a CalculatorPackage class that implements ReactPackage and registers the module to createNativeModules, then add that package to MainApplication.

Calling the Module from JavaScript

NativeModules

On the JavaScript side, the module is called via NativeModules with the same name:

JSCalling a native module from JS
import { NativeModules } from "react-native";
 
const { Calculator } = NativeModules;
 
async function hitung() {
  try {
    const hasil = await Calculator.tambah(2, 3);
    console.log("Hasil:", hasil);
  } catch (error) {
    console.error("Gagal memanggil module", error);
  }
}

Calculator.tambah(2, 3) returns a promise that resolves to the native result. A rebuild is required after changing native code — Fast Refresh doesn't reach this layer.

Creating TypeScript Typings

Define typings so the editor validates module calls:

JSTyping for a native module
import { NativeModules } from "react-native";
 
interface CalculatorInterface {
  tambah(a: number, b: number): Promise<number>;
}
 
export const Calculator = NativeModules.Calculator as CalculatorInterface;

With the typings above, type errors are caught during development, not when the app crashes on a user's device.

Camera, Biometric, and Maps

Three modules that production apps often need — all of them native modules wrapped in a JavaScript API:

Install community modules
npm install react-native-vision-camera
npm install react-native-biometrics
npm install react-native-maps
  • react-native-vision-camera: camera with frame control, covered in episode 12.
  • react-native-biometrics: fingerprint and Face ID authentication, covered in episode 15.
  • react-native-maps: native Apple Maps and Google Maps.

Matching with New Architecture Support

Before installing, check whether the library already uses JSI or still depends on interop. Modern libraries like vision-camera and Reanimated run on JSI and offer far better performance.

Tip

Writing your own native module is only necessary when the feature doesn't exist in the ecosystem yet. For common needs, choose a well-maintained library — writing and maintaining a native module is a long-term burden.

Closing

Episode 10 opened the native black box: the legacy asynchronous bridge replaced by JSI-based TurboModules, the interop layer keeping old libraries running, and now you can write a Kotlin module and call it from JavaScript.

Key takeaways:

  • The legacy bridge serializes JSON and is async; TurboModules are direct via JSI.
  • Native modules are loaded lazily for faster startup.
  • The interop layer adapts legacy libraries, but without a performance boost.
  • @ReactMethod and getName() are the contract between native and JS.
  • NativeModules calls modules; a native rebuild is required after changes.
  • Choose community modules that already support JSI for the best performance.

In the next episode, episode 11, we'll discuss push notifications and background tasks: FCM for Android and APNs for iOS, the permission flow, notification handlers, and react-native-background-fetch with a battery-efficient strategy.

Learn React Native - Native Modules & TurboModules | Learn React Native