Bridge the Gap How to Write Custom Native Modules for React Native 2026

Bridge the Gap: How to Write Custom Native Modules for React Native 2026

React Native has changed the way developers build mobile applications by allowing JavaScript and TypeScript code to power experiences across iOS and Android. However, even a powerful cross-platform framework cannot expose every native capability through a single JavaScript API. When your application needs access to a platform-specific API, an existing native library, high-performance processing, or functionality that React Native does not provide, custom native modules for React Native can bridge the gap.

Native modules provide a connection between your JavaScript application and native platform code. In modern React Native development, the preferred approach is the New Architecture, which uses Turbo Native Modules and Codegen. React Native’s documentation explains that the New Architecture has been enabled by default in new projects since React Native 0.76.

This guide explains what React Native native modules are, when you need them, how Turbo Native Modules work, and the practical steps involved in creating a custom native module for Android and iOS.

What Is a Native Module in React Native?

A React Native native module is a piece of native code that exposes functionality to JavaScript or TypeScript.

Imagine that your React Native application needs access to a device feature that is not available through the React Native core APIs or a suitable third-party library. Instead of rebuilding the entire application in native Android or iOS code, you can create a native module that exposes only the required functionality.

For example, a custom native module could provide access to:

  • Advanced device APIs
  • Bluetooth functionality
  • Secure storage
  • Native payment systems
  • Background processing
  • Specialized sensors
  • Existing Java, Kotlin, Swift, Objective-C, or C++ libraries
  • High-performance image or data processing
  • Platform-specific enterprise services

React Native describes native modules as libraries without a user interface that can expose native functionality such as storage, notifications, and network events to JavaScript.

The concept is simple:

JavaScript/TypeScript → Turbo Native Module → Native Platform API

This architecture allows developers to keep most of an application cross-platform while still accessing the full capabilities of the underlying operating system.

When Should You Create a Custom Native Module?

Creating a native module should not be your first option for every feature.

Before writing native code, check whether React Native already provides the required API or whether a well-maintained community library solves the problem. React Native’s official library documentation recommends searching React Native Directory and the npm ecosystem before building functionality yourself.

A custom native module becomes attractive when:

  1. The required platform API is unavailable in React Native.
  2. Existing libraries do not support your React Native version.
  3. You need functionality from an existing native SDK.
  4. You need specialized native performance.
  5. You require a feature that is unique to your application.
  6. You need precise control over native implementation details.

For example, React Native’s documentation uses access to native calendar APIs as a reason to create a native module. JavaScript can call the module, while the module communicates with the native calendar system.

Native Modules and the React Native New Architecture

One of the most important considerations in modern React Native native module development is the New Architecture.

Older React Native applications commonly used the legacy Native Module system. Modern React Native development uses Turbo Native Modules, which are designed to work with the New Architecture.

The official React Native documentation describes the process as a combination of a typed JavaScript specification, Codegen, and native implementation. Codegen converts the specification into native interfaces that your Android and iOS code can implement.

The general workflow is:

TypeScript specification → Codegen → Native interfaces → Android/iOS implementation → JavaScript API

This approach provides a clearer contract between JavaScript and native code.

It also reduces the amount of manually written bridge code developers previously needed to maintain.

Step 1: Define the JavaScript Specification

The first important step when creating a Turbo Native Module is defining its JavaScript or TypeScript specification.

Suppose you want to create a simple native storage module with two operations:

  • Save a value
  • Retrieve a value

A simplified specification could look like this:

import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';

export interface Spec extends TurboModule {
  setItem(value: string, key: string): void;
  getItem(key: string): string | null;
}

export default TurboModuleRegistry.getEnforcing<Spec>(
  'NativeLocalStorage',
);

The specification describes the API that JavaScript expects to use.

This is an important design principle: define the contract before implementing the native functionality.

The native Android and iOS implementations should follow that contract rather than exposing completely different APIs to JavaScript.

Step 2: Configure Codegen

Codegen is a central part of Turbo Native Module development.

Instead of manually creating every interface that connects JavaScript and native code, React Native can generate native scaffolding from your typed specification.

The official workflow consists of defining a typed JavaScript specification, configuring dependency management to run Codegen, writing application code according to the specification, and then implementing the generated native interfaces.

This approach becomes especially valuable as your native module grows.

If you change the TypeScript specification, the generated native interfaces can reflect those changes, helping keep the JavaScript and native layers synchronized.

Step 3: Implement the Module on Android

Android native development for React Native can use Java or Kotlin.

For example, a Turbo Native Module can implement a generated interface such as NativeLocalStorageSpec.

A simplified Kotlin structure looks like this:

class NativeLocalStorageModule(
    reactContext: ReactApplicationContext
) : NativeLocalStorageSpec(reactContext) {

    override fun getName(): String {
        return NAME
    }

    override fun setItem(value: String, key: String) {
        // Native Android implementation
    }

    override fun getItem(key: String): String? {
        // Read from native storage
        return null
    }

    companion object {
        const val NAME = "NativeLocalStorage"
    }
}

The official React Native Android Turbo Module guide demonstrates this architecture using Java and Kotlin implementations and a generated NativeLocalStorageSpec interface.

The module then needs to be registered so React Native can discover it at runtime.

This registration step is essential. A correctly written class is not enough if the React Native runtime does not know that the module exists.

Step 4: Register the Android Module

The module is generally exposed through a React package.

A simplified Kotlin package structure can look like this:

class NativeLocalStoragePackage : BaseReactPackage() {

    override fun getModule(
        name: String,
        reactContext: ReactApplicationContext
    ): NativeModule? {
        return if (name == NativeLocalStorageModule.NAME) {
            NativeLocalStorageModule(reactContext)
        } else {
            null
        }
    }
}

The package is then added to the React Native application’s package list.

The official Android Turbo Native Module documentation explains that the package provides the module to the React Native runtime and shows how it can be registered in the application’s package configuration.

For reusable libraries, modern React Native tooling can also support autolinking, reducing the need for developers to manually register every dependency.

Step 5: Implement the iOS Native Module

On iOS, developers can work with Swift, Objective-C, and Objective-C++ depending on the module architecture and requirements.

Swift is Apple’s official and default language for modern iOS development. React Native provides documentation for using Swift with Turbo Native Modules. Because React Native’s core uses C++, the Swift integration can require Objective-C++ glue code and a bridging header.

This means that an iOS native module may involve several layers:

TypeScript → Generated interface → Objective-C++ bridge → Swift implementation → iOS API

The exact structure depends on the module and React Native version.

For developers already comfortable with Swift, this approach makes it possible to reuse existing iOS code without rewriting everything in JavaScript.

Why Codegen Matters

Codegen is more than a convenience tool. It helps establish a predictable boundary between JavaScript and native code.

Without a clear interface, a large native module can become difficult to maintain. JavaScript developers may expect one function signature while Android and iOS implementations behave differently.

With a specification-driven workflow, the intended API becomes explicit.

For example:

interface Spec extends TurboModule {
  calculateTotal(price: number, quantity: number): number;
}

Both platforms are expected to implement the same logical contract.

This is particularly useful for teams where JavaScript, Android, and iOS developers work independently.

Using Swift, Kotlin, and C++ With React Native

A major advantage of custom native modules is flexibility.

Android developers can use Kotlin or Java for platform-specific functionality. iOS developers can use Swift and Objective-C++ where required. For platform-independent native logic, C++ can also be an option.

React Native’s documentation specifically describes pure C++ Turbo Native Modules as a way to share platform-agnostic native logic between Android and iOS. The documented workflow includes creating JavaScript specifications, configuring Codegen, implementing native logic, registering the module on both platforms, and testing it from JavaScript.

C++ can therefore be particularly interesting for computationally intensive functionality that should behave consistently across platforms.

However, C++ also introduces additional complexity. If the feature can be implemented cleanly in Kotlin and Swift, using separate platform implementations may be easier for the development team to maintain.

Native Modules vs Native Components

It is important not to confuse Native Modules with Native Components.

A native module generally exposes functionality without directly representing a user interface. Examples include storage, notifications, device services, and native processing.

A Native Component, on the other hand, represents a native user-interface element that needs to be rendered and controlled from React Native.

React Native’s documentation distinguishes these two concepts and explains that Native Modules provide native functionality while Native Components expose native platform views and controllers through React components.

If your goal is to expose a native UI widget, you may need a Fabric Native Component rather than a Turbo Native Module.

Testing a Custom Native Module

Testing should happen at multiple levels.

First, test the native implementation independently where practical. Then test the module through JavaScript or TypeScript.

For example:

const result = NativeLocalStorage.getItem('username');

console.log(result);

Your tests should verify:

  • The module loads correctly.
  • Methods return expected values.
  • Invalid input is handled safely.
  • Android and iOS behave consistently.
  • Errors are predictable.
  • The module works in development builds.
  • The module works in production builds.
  • The module survives application restarts when persistence is expected.

Do not rely only on a simulator. Native behavior can depend on real device hardware, operating-system permissions, background restrictions, and device-specific services.

Common Mistakes When Building Native Modules

One common mistake is writing native code before designing the JavaScript API.

Start with the interface. Ask what the JavaScript developer should be able to call and what the expected result should be.

Another mistake is creating different APIs for Android and iOS. Platform implementations may differ internally, but the public JavaScript API should ideally remain consistent.

Developers should also avoid unnecessary native modules. Every custom module introduces another layer that must be tested, documented, upgraded, and maintained.

Finally, pay attention to React Native version compatibility. Native APIs and architecture-related tooling evolve over time, so code copied from older tutorials may not represent the recommended approach for a current React Native project.

React Native’s current documentation explicitly identifies legacy Native Modules as older APIs and recommends the New Architecture approach with Turbo Native Modules for modern development.

How to Package a Native Module for NPM

Once your module works inside your application, you may want to distribute it as an npm package.

React Native’s documentation explains that native modules can be packaged as npm libraries containing JavaScript plus native code for each supported platform. The official documentation also points developers toward create-react-native-library as a way to bootstrap a native library project.

A reusable package should ideally include:

  • Clear installation instructions
  • Supported React Native versions
  • Android requirements
  • iOS requirements
  • TypeScript definitions
  • Example usage
  • API documentation
  • Error-handling information
  • Testing instructions
  • Changelog and versioning information

Good documentation is particularly important for native libraries because installation problems can occur at the native build level rather than in JavaScript.

Security and Privacy Considerations

Native modules can access powerful platform capabilities, which means security should be considered from the beginning.

Avoid exposing unnecessary native APIs to JavaScript. A module should provide the minimum functionality required by the application.

If your module handles sensitive information, consider:

  • Secure storage
  • Access control
  • Input validation
  • Safe error messages
  • Minimal data collection
  • Proper permission handling
  • Avoiding sensitive information in logs

The fact that native code can access powerful operating-system APIs does not mean an application should expose all of those APIs through JavaScript.

A smaller and carefully designed interface is usually easier to secure and maintain.

Performance: When Native Code Makes Sense

Performance is another reason developers sometimes choose native modules.

JavaScript is highly capable, but certain workloads can benefit from native implementation. Examples include intensive image processing, specialized algorithms, hardware integration, and some computational workloads.

However, native code is not automatically faster for every task.

Moving data between JavaScript and native layers also has a cost. If an operation repeatedly transfers large objects between environments, the communication overhead can reduce the expected performance benefit.

The best approach is to measure.

Profile your JavaScript implementation first, identify the actual bottleneck, and then determine whether native code can solve it efficiently.

Final Thoughts

Writing a custom native module for React Native is one of the most powerful techniques available to developers who need functionality beyond the standard JavaScript APIs.

The modern approach centers on Turbo Native Modules, TypeScript specifications, Codegen, and platform-specific implementations. Instead of abandoning React Native whenever a native feature is required, developers can create a carefully designed bridge between their cross-platform application and the underlying Android or iOS platform.

The key is knowing when to use this technique.

Start by checking React Native’s built-in APIs and established community libraries. If they cannot provide what your application needs, define a clean JavaScript interface, use Codegen, implement the required Android and iOS functionality, test it thoroughly, and document the result.

For teams building sophisticated mobile applications, custom native modules can provide the best of both worlds: the productivity of React Native and the capabilities of native mobile development.

Most importantly, treat the native layer as a long-term part of your architecture rather than a quick workaround. A well-designed module can become a reusable foundation for your application, while a poorly designed one can become technical debt. The difference usually comes down to a clear API, careful platform integration, strong testing, and keeping up with the React Native New Architecture.

Frequently Asked Questions

What is a custom native module in React Native?

A custom native module is native Android or iOS code exposed to JavaScript or TypeScript so a React Native application can access functionality that is not directly available through its standard APIs.

What is a Turbo Native Module?

A Turbo Native Module is the modern New Architecture approach for exposing native functionality to React Native JavaScript code. It uses a typed specification and Codegen to generate native interfaces.

Can I write a React Native native module with Kotlin?

Yes. React Native’s official Turbo Native Module documentation provides Android examples using both Java and Kotlin.

Can React Native native modules use Swift?

Yes. React Native provides guidance for implementing Turbo Native Modules with Swift, although Swift implementations may require Objective-C++ interoperability code and a bridging header.

Should every React Native developer learn native development?

Not necessarily. Many React Native applications can be built primarily with JavaScript or TypeScript. However, understanding native development becomes increasingly valuable when working with advanced device APIs, performance-sensitive features, custom SDK integrations, or complex native libraries.

Are legacy Native Modules still relevant?

Existing applications may continue using legacy modules, and React Native provides interoperability support for many older libraries. However, the current direction of React Native development is the New Architecture, including Turbo Native Modules and Fabric Native Components.

Trusted Sources

For the most accurate and current information, developers should prioritize official React Native documentation, particularly the guides covering Native Modules, Turbo Native Modules, Codegen, Android, iOS, and the New Architecture. React Native’s documentation was updated in 2026 and reflects the framework’s ongoing architectural transition.

Recommended official resources include the React Native Native Modules documentation, React Native New Architecture documentation, and Turbo Native Modules for Android.

Leave a Reply

Your email address will not be published. Required fields are marked *

Solverwp- WordPress Theme and Plugin