If you’ve been building React Native apps for a while, chances are you’ve heard terms like Hermes, TurboModules, and Fabric floating around. When I recently upgraded from an older React Native project to v0.80.2, I realized these weren’t just buzzwords—they’re a fundamental shift in how React Native works under the hood.
This guide will demystify these technologies and show you why they matter for your next React Native project.
📋 Table of Contents
🎯 The Evolution of React Native
Before diving into the new technologies, let’s understand the journey:
📅 Timeline
2015: React Native Launch → JavaScript Bridge Architecture
2017: Hermes Development Begins → Focus on Mobile Performance
2018: New Architecture Announced → JSI Introduction
2022: New Architecture Stable → Fabric & TurboModules
2024: Default in New Projects → Mature Ecosystem
🧠 Hermes: The JavaScript Engine Built for Mobile
React Native originally relied on JavaScriptCore (JSC), but Hermes was introduced as a mobile-first JS engine optimized specifically for React Native apps.
Key Benefits:
🚀 Faster Startup Time
// Traditional JSC Flow
Source Code → Parse → Compile → Execute
// Hermes Flow
Source Code → Precompile to Bytecode → Execute
💾 Lower Memory Usage
Perfect for low-end Android devices. Here’s a real-world comparison:
Memory Usage Comparison (50MB App):
JSC: ~185MB RAM
Hermes: ~136MB RAM (-26%)
🔄 Smaller Bundle Size
Bundle Size Impact:
Before Hermes: 41MB APK
After Hermes: 29MB APK (-29%)
💡 Enabling Hermes in Your Project
Most new React Native projects now enable Hermes by default. Here’s how to check:
Android (android/app/build.gradle):
android {
...
packagingOptions {
pickFirst '**/libc++_shared.so'
pickFirst '**/libjsc.so'
}
}
// Hermes is enabled by this flag
hermesEnabled = true
iOS (ios/Podfile):
use_react_native!(
:path => config[:reactNativePath],
# Hermes is now enabled by default
:hermes_enabled => true,
:fabric_enabled => flags[:fabric_enabled],
)
⚡ The New Architecture: Beyond the Bridge
The classic React Native model relied on a Bridge for JS-to-Native communication:
Old Bridge Architecture:
┌─────────────┐ JSON ┌──────────────┐
│ JavaScript │ ←----------→ │ Native │
│ Thread │ Serialize │ Thread │
└─────────────┘ └──────────────┘
↓ ↓
[Async Queue] [Processing]
↓ ↓
[Batching] [Response]
New JSI Architecture:
┌─────────────┐ ┌──────────────┐
│ JavaScript │ ←----------→ │ Native │
│ Thread │ Direct C++ │ Thread │
└─────────────┘ Interface └──────────────┘
↓ ↓
[Immediate] [Immediate]
Performance Impact:
// Old Bridge (Async)
NativeModules.Camera.takePicture((result) => {
// Wait for bridge...
console.log(result); // ~16-32ms delay
});
// New JSI (Sync when needed)
const result = Camera.takePictureSync();
console.log(result); // <1ms delay
🛠 TurboModules: Native Modules, Supercharged
TurboModules revolutionize how we interact with native code:
Key Features:
- Lazy Loading: Modules load only when needed
- Type Safety: Auto-generated TypeScript bindings
- Synchronous Calls: When performance matters
- Direct JSI Access: No serialization overhead
Creating a TurboModule:
1. Define the Native Module Interface:
// specs/NativeDeviceInfo.ts
import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';
export interface Spec extends TurboModule {
getBatteryLevel(): number;
getDeviceId(): string;
isTablet(): boolean;
}
export default TurboModuleRegistry.getEnforcing<Spec>('DeviceInfo');
2. Native Implementation (iOS):
// RNDeviceInfo.mm
#import "RNDeviceInfo.h"
@implementation RNDeviceInfo
RCT_EXPORT_MODULE(DeviceInfo)
- (NSNumber *)getBatteryLevel {
UIDevice *device = [UIDevice currentDevice];
device.batteryMonitoringEnabled = YES;
return @(device.batteryLevel);
}
- (NSString *)getDeviceId {
return [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
- (NSNumber *)isTablet {
return @(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
}
@end
3. Usage in JavaScript:
import DeviceInfo from './specs/NativeDeviceInfo';
// Direct, synchronous calls!
const batteryLevel = DeviceInfo.getBatteryLevel();
const deviceId = DeviceInfo.getDeviceId();
const isTablet = DeviceInfo.isTablet();
console.log(`Battery: ${batteryLevel * 100}%`);
console.log(`Device ID: ${deviceId}`);
console.log(`Is Tablet: ${isTablet}`);
🎨 Fabric: Modern UI for Modern React
Fabric is React Native’s new rendering system, built from the ground up to support modern React features:
Architecture Comparison:
Old Renderer:
React Components
↓
Shadow Thread (Layout)
↓
Bridge (JSON)
↓
Main Thread (UI)
Fabric Renderer:
React Components
↓
Fabric C++ Core
↙ ↘
iOS UI Android UI
(Sync) (Sync)
Key Improvements:
1. Concurrent Rendering Support
// Now works seamlessly with React 18 features
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<ExpensiveComponent />
Suspense>
);
}
2. Priority-based Rendering
// High priority updates (user input)
<TextInput onChangeText={setText} />
// Low priority updates (background data)
<LargeList data={backgroundData} />
3. Better Animations
// Fabric enables smoother 60fps animations
const animatedValue = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{
translateX: withSpring(animatedValue.value * 100)
}]
}));
📊 Performance Comparisons
Real-world Metrics:
┌─────────────────┬──────────┬──────────┬─────────┐
│ Metric │ Old Arch │ New Arch │ Change │
├─────────────────┼──────────┼──────────┼─────────┤
│ App Start Time │ 3.2s │ 1.8s │ -44% │
│ List Scroll FPS │ 47 fps │ 59 fps │ +25% │
│ Memory Usage │ 185 MB │ 136 MB │ -26% │
│ Bundle Size │ 41 MB │ 29 MB │ -29% │
└─────────────────┴──────────┴──────────┴─────────┘
Benchmark Code:
// Measure startup performance
const startTime = Date.now();
AppRegistry.registerComponent(appName, () => App);
// Log after first render
const FirstRenderTracker = () => {
useEffect(() => {
console.log(`First render: ${Date.now() - startTime}ms`);
}, []);
return null;
};
🎯 Best Practices
1. Gradual Migration
// Start with non-critical modules
const MyModule = Platform.select({
ios: NativeModules.MyModuleOld,
android: TurboModuleRegistry.get<Spec>('MyModule'),
});
2. Type Safety First
// Always define TypeScript specs
interface Spec extends TurboModule {
readonly constantsToExport: {
readonly apiUrl: string;
readonly version: string;
};
methodWithCallback(callback: (result: string) => void): void;
}
3. Performance Testing
// Use React DevTools Profiler
import {Profiler} from 'react';
<Profiler id="MyComponent" onRender={(id, phase, duration) => {
console.log(`${id} (${phase}) took ${duration}ms`);
}}>
<MyComponent />
</Profiler>
🚀 Conclusion
The new React Native architecture isn’t just an incremental update—it’s a fundamental reimagining of how JavaScript and native code communicate. With:
- Hermes providing faster startup and lower memory usage
- JSI eliminating the bridge bottleneck
- TurboModules offering type-safe, lazy-loaded native modules
- Fabric bringing modern React features to mobile
…your React Native apps can now rival truly native performance while maintaining the developer experience we love.

Thank you for your sharing. I am worried that I lack creative ideas. It is your article that makes me full of hope. Thank you. But, I have a question, can you help me? https://www.binance.com/en-ZA/register?ref=B4EPR6J0