
How to Build Native Modules in React Native Without Losing Your Mind (A Step-by-Step Guide)
- Larry Brooks
- Software, Technology
- 11 Jun, 2026
You need a feature that requires native platform access. You search npm. Nothing exists, or what exists is unmaintained, two major versions behind, and last updated 18 months ago. You need to write a native module.
This is the moment most React Native developers panic. It should not be.
What a Native Module Actually Is
A native module is a bridge between your JavaScript code and platform-native APIs. On iOS, it is an Objective-C or Swift class that exposes methods to JavaScript. On Android, it is a Kotlin or Java class that does the same. The React Native bridge (or JSI, in the New Architecture) handles the communication.
The concept is straightforward. The friction comes from unfamiliar tooling — Xcode projects, Gradle configurations, platform-specific language syntax — not from conceptual complexity.
The iOS Side
Create a new Objective-C file in your Xcode project. Import React/RCTBridgeModule.h. Declare your class as implementing RCTBridgeModule. Use the RCT_EXPORT_MODULE() macro to register it. Use RCT_EXPORT_METHOD() to expose individual methods to JavaScript.
For the New Architecture with TurboModules, create a TypeScript spec file that defines the module's interface using TurboModuleRegistrySpec. The codegen tool generates the Objective-C++ bridging code from this spec. Your implementation conforms to the generated protocol.
The most common mistake on iOS: forgetting to add the implementation file to the correct build target. If your module exists but JavaScript cannot find it, check the target membership in Xcode's file inspector.
The Android Side
Create a Kotlin class that extends ReactContextBaseJavaModule. Override getName() to return the module name that JavaScript will use. Annotate methods with @ReactMethod to expose them. Create a Package class that registers your module and add it to getPackages() in your Application class.
For TurboModules, the same TypeScript spec file drives codegen for both platforms. The generated Java interface defines the contract your Kotlin implementation must fulfill.
The most common mistake on Android: not adding the package to the application's package list. The module compiles successfully but is undefined at runtime.
The JavaScript Side
With the classic architecture, access your module through NativeModules.YourModuleName. With TurboModules, import the generated spec and call methods directly. TurboModules are lazily loaded — they initialize only when first accessed, which improves startup time.
For methods that return values asynchronously, use promises on both platforms. For methods that emit events over time, implement RCTEventEmitter on iOS or RCTDeviceEventEmitter on Android and subscribe in JavaScript with NativeEventEmitter.
The Testing Strategy
Test native modules at three levels. Unit test the native code using XCTest (iOS) and JUnit (Android). Integration test the bridge communication using Jest mocks that verify the correct native methods are called with the correct arguments. End-to-end test the full flow using Detox or Maestro on real devices.
Writing your first native module takes a day. Writing your fifth takes an hour. The skill is learnable and it removes the most significant limitation of React Native development. Let's build the native capabilities your app needs.
