Sep 23, 2025

Bridging the Gap: Mastering Turbo Modules and Native Linking in React Native

Leverage React Native’s full potential with Turbo Modules & Native Linking. Improve performance, access native features, and build scalable, high-performing apps.

Author

Mehar MiddhaMehar MiddhaSoftware Engineer - II
Bridging the Gap: Mastering Turbo Modules and Native Linking in React Native

Ever hit a wall in React Native, wishing you could tap directly into platform-specific magic or squeeze out every drop of performance?

That's precisely where I found myself, pushing the boundaries of what a cross-platform app could do. While React Native is fantastic for rapid development and shared codebases, there are moments when native power is indispensable. I'm excited to share my journey into mastering Turbo Modules and Native Linking, and how these advanced features allowed me to unlock the full potential of my React Native applications.

This isn't just a theoretical discussion; it's a look at how I tackled real-world performance bottlenecks and integrated features that simply aren't possible with JavaScript alone. It’s about seamlessly blending the efficiency of native code with the flexibility of React Native, solving tricky puzzles, and having a blast along the way.

The Core Challenge: Unleashing Native Power in a Cross-Platform World

Building robust React Native applications often means encountering scenarios where JavaScript, no matter how optimized, just can't keep up with the demands of highly intensive tasks or direct hardware access. The main goal was to connect the JavaScript world with the native world – be it Swift/Objective-C on iOS or Kotlin/Java on Android – in the most efficient way possible.

The key challenges were:

  1. Performance Critical Operations: For tasks like complex image processing, heavy data encryption, or high-frequency sensor access, native code offers superior performance.
  2. Platform-Specific Features: Accessing unique device capabilities (e.g., specific NFC readers, custom biometric hardware) often requires direct native module implementation.
  3. Seamless Integration: Ensuring that these native bridges are stable, maintainable, and don't introduce unnecessary overhead.
  4. Type Safety (Turbo Modules): Addressing the traditional untyped nature of Native Modules for better developer experience and fewer runtime errors.

Bridging the Gap: The Evolution of Native Linking

Before diving into Turbo Modules, it's crucial to understand the foundation: Native Linking (often implicitly handled by react-native link or autolinking). This is the traditional way to expose native capabilities to your JavaScript code. You write native code (e.g., a Swift class in iOS, a Java class in Android) that performs a specific task, and then you "export" methods from that native module so they can be called from your React Native JavaScript.

This approach works well for many use cases. For example, if you wanted to implement a custom Toast message or access a device's calendar, you'd create a NativeModule.

Here’s a conceptual look at how a simple native module might be exposed (simplified):

iOS (Objective-C/Swift Header)

Objective-C
// MyCustomModule.h
#import <React/RCTBridgeModule.h>

@interface MyCustomModule : NSObject <RCTBridgeModule>
@end

iOS (Objective-C/Swift Implementation)

Objective-C
// MyCustomModule.m
#import "MyCustomModule.h"

@implementation MyCustomModule

RCT_EXPORT_MODULE(); // Exposes module to JS as 'MyCustomModule'

RCT_EXPORT_METHOD(showAlert:(NSString *)message) {
  // Native code to show alert
  dispatch_async(dispatch_get_main_queue(), ^{
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"React Native Alert"
                                                                   message:message
                                                            preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
    UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
    [root presentViewController:alert animated:YES completion:nil];
  });
}

@end

Android (Java/Kotlin)

Java
// MyCustomModule.java
package com.mycustommodule;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import android.widget.Toast;

public class MyCustomModule extends ReactContextBaseJavaModule {
    MyCustomModule(ReactApplicationContext context) {
        super(context);
    }

    @Override
    public String getName() {
        return "MyCustomModule"; // Exposes module to JS as 'MyCustomModule'
    }

    @ReactMethod
    public void showToast(String message) {
        Toast.makeText(getReactApplicationContext(), message, Toast.LENGTH_SHORT).show();
    }
}

React Native (JavaScript)

JavaScript
import { NativeModules } from 'react-native';
const { MyCustomModule } = NativeModules;

// Call native method
MyCustomModule.showAlert('Hello from React Native!'); // For iOS
MyCustomModule.showToast('Hello from React Native!'); // For Android

This traditional NativeModules approach works via the "Bridge," which serializes and deserializes data between JavaScript and native threads. While effective, this can introduce overhead for frequent, high-volume calls.

The Next Frontier: Turbo Modules for Enhanced Performance

This is where Turbo Modules come in as a significant evolution. Introduced as part of React Native's "New Architecture," Turbo Modules aim to reduce the overhead of the traditional bridge, offering:

  • JSI (JavaScript Interface): Instead of asynchronous serialization over the Bridge, JSI allows JavaScript to directly invoke native methods synchronously. This means faster communication.
  • Type Safety: Turbo Modules leverage a shared interface definition (written in TypeScript or Flow) which generates native code. This ensures type consistency between JS and native, catching errors at compile time rather than runtime.
  • Lazy Loading: Native modules can be loaded only when they are needed, improving app startup times.

Implementing a Turbo Module involves defining a spec in JavaScript/TypeScript, and then implementing the native code for iOS and Android according to that spec. The codegen automatically creates the necessary native bridging code.

Here’s a conceptual overview of the Turbo Module flow:

  1. Define JavaScript Spec: Create a TypeScript file (NativeMyTurboModule.ts) defining the module's interface.
  2. Generate Native Code: React Native's Codegen uses this spec to generate native interface files.
  3. Implement Native Code: Write the actual iOS (Swift/Objective-C) and Android (Kotlin/Java) code conforming to the generated interfaces.
TypeScript
// NativeMyTurboModule.ts (JavaScript Spec)
import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
import { TurboModuleRegistry } from 'react-native';

export interface Spec extends TurboModule {
  // Method to add two numbers
  add(a: number, b: number): number;
  // Method with a callback
  sayHello(name: string, callback: (message: string) => void): void;
}
// Register your Turbo Module
export default TurboModuleRegistry.get<Spec>('MyTurboModule') as Spec | null;

With this, the communication is much more direct and performant, making Turbo Modules ideal for performance-critical scenarios or features that need to interact heavily with native APIs.

What I Learned and What You Can Build

Diving into Turbo Modules and Native Linking was an incredible learning curve, requiring a deeper understanding of both JavaScript and native platform development. It showcased how React Native can truly be a powerful framework for building high-performance, platform-specific applications.

Building with these concepts, I gained significant experience in:

  • Deepening Native Understanding: A forced but welcome dive into iOS (Swift/Objective-C) and Android (Kotlin/Java) development paradigms.
  • Performance Optimization: Learning to identify bottlenecks and strategically offload heavy computations to the native side.
  • Robust Error Handling: Ensuring seamless error propagation between native and JavaScript layers.
  • Type-Safe Bridging: Appreciating how Turbo Modules improve developer experience and reduce runtime bugs through strong typing.

This isn't just about unlocking low-level access; it's about the power to:

  • Build Truly Unique Features: Implement highly specialized functionalities that require direct hardware or OS interaction.
  • Achieve Native-Like Performance: Deliver buttery-smooth user experiences even for demanding tasks.
  • Future-Proof Your Apps: Embrace React Native's New Architecture for better maintainability and scalability.

The journey of seamlessly blending JavaScript and native capabilities has truly transformed my approach to React Native development. I hope this deep dive helps you unlock new possibilities in your own projects!

Subscribe to Our Newsletter

RELATED ARTICLES

More from the engineering frontline.

Dive deep into our research and insights on design, development, and the impact of various trends to businesses.
The Bug That Doesn't Show Up in Code Review: Why Your Flutter Web App Reloads on Safari
The Bug That Doesn't Show Up in Code Review: Why Your Flutter Web App Reloads on Safari
A real-world look at how oversized images can trigger Safari reloads and iOS crashes in Flutter apps and how smarter image decoding prevents them.
From Prompting to Process: What Changed When Flutter Shipped Agent Skills
From Prompting to Process: What Changed When Flutter Shipped Agent Skills
This blog explores how Flutter Agent Skills improve AI-assisted development by combining official framework workflows with project-specific guidance for more consistent development.
Why Everything Your AI Builds Looks the Same
Why Everything Your AI Builds Looks the Same
This blog explores why AI-generated interfaces often look alike and explains how design systems, product context, and reusable engineering practices help teams build distinctive, scalable
How We Built the Missing Bridge from Code to Figma
Technology

Jul 10, 2026

How We Built the Missing Bridge from Code to Figma
This blog explores how AI-generated React apps get turned into fully editable, designer-ready Figma files by reading React Fiber instead of the DOM.
Building a Resilient Hybrid-Cloud Network with WireGuard HA, Route-Based Failover, and Deep Observability
Technology

Jun 27, 2026

Building a Resilient Hybrid-Cloud Network with WireGuard HA, Route-Based Failover, and Deep Observability
A practical breakdown of building resilient AWS-to-on-premises connectivity with WireGuard HA, active-standby failover, and deep packet-forwarding observability.
We Built a 114-Second AWS-to-Azure Failover. Here’s What We Learned
Technology

Jun 19, 2026

We Built a 114-Second AWS-to-Azure Failover. Here’s What We Learned
A practical guide to building a 114-second multi-cloud disaster recovery failover between AWS and Azure — what we built, what broke, and what we learned.
Cloud-Native and Cloud-Agnostic Are Not Ideologies; They Are Business-Stage Decisions
Technology

Jun 12, 2026

Cloud-Native and Cloud-Agnostic Are Not Ideologies; They Are Business-Stage Decisions
This blog explains how organizations can balance speed, scalability, and operational flexibility as they grow from startup to enterprise scale.

The Right Conversation Can Save You Six Months.

Whether you’re navigating AI adoption, modernizing legacy systems, or scaling a product - we start by listening. No pitch deck. No template. A real conversation.