Optimizing React Native Startup with TurboModules and JSI
Learn how to reduce React Native startup time using TurboModules and JSI to replace the legacy bridge and enable lazy native loading.
09 Jan 2026, 08:35 UTC

Most React Native applications suffer from a sluggish "cold start" as they grow. In the legacy architecture, every native module—ranging from camera access APIs to secure storage—must be initialized at the moment the app launches, even if the user never opens those features. This global initialization creates a bottleneck on the JavaScript thread, delaying the rendering of the first screen.
The React Native New Architecture solves this through TurboModules. By replacing the asynchronous JSON bridge with the JavaScript Interface (JSI), TurboModules allow for lazy loading. This means native code is only loaded and initialized when explicitly called, drastically reducing memory overhead and startup time.
The End of the JSON Bridge
In the old model, communication between JavaScript and native code (Java/Objective-C) required asynchronous message passing. Data had to be serialized into JSON, sent across the bridge, and then deserialized on the other side. This process is computationally expensive and prevents synchronous execution.
JSI changes this paradigm by allowing JavaScript to hold a direct reference to C++ host objects. Because the memory is shared directly, JavaScript can call native methods as if they were local asynchronous functions. This eliminates the serialization overhead entirely and enables synchronous operations, which are critical for performance-sensitive tasks.
Implementing Your First TurboModule
To create a TurboModule, you cannot simply write a standard class. You must define a schema that the Codegen tool uses to generate the C++ glue code. This ensures type safety between TypeScript and the native layers.
Below is a conceptual example of defining a TypeScript interface for a Device Information module. This interface acts as the source of truth.
// DeviceInfoSpec.ts
import { TurboModule, TurboRegistry } from 'react-native';
export interface DeviceInfo extends TurboModule {
getModel(): string;
getBatteryLevel(): number;
}
export default TurboRegistry.getEnDeviceInfo');
Once defined, you run the Codegen tool (usually via the build process), which generates the C++ headers that you must then implement in Android (Java/Kotlin) and iOS (Objective-C++).
Configuration and Verification
To use TurboModules, you must explicitly enable the New Architecture in your project. This is not the default in all versions.
Android Setup
Edit your android/gradle.properties file and set:
newArchEnabled=true
iOS Setup
Run the pod install command with the specific flag to update your Podfile:
cd ios RCT_ENABLE_NEW_ARCH=1 pod install cd ..
To verify that the architecture is active, check the build logs during compilation. On Android, look for logs related to the Fabric renderer being initialized. On iOS, you can use the debugger to see if the JSI-based objects are being instantiated instead of the old legacy bridge methods.
The Performance Trade-off
While TurboModules offer significant performance gains, they come with a cost in complexity. Because JSI allows for synchronous calls, a poorly written native function can block the JavaScript thread. If your native code performs a heavy computation or a synchronous I/O operation without threading, the UI will freeze, leading to dropped frames.
Furthermore, the migration requires all third-party dependencies to support the New Architecture. If your app relies on older libraries that haven't moved to TurboModules, you will still be burdened by legacy bridge initialization, partially negating the startup benefits.
Transitioning to TurboModules is a strategic decision for large-scale apps. Start by identifying your heaviest native-initialized modules and migrating them one by one to reclaim your app's time-to-first-paint (TTFP).
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.