DEV Community

Cover image for Understanding Hermes, Fabric, and the New Architecture in React Native
Nader Alfakesh
Nader Alfakesh

Posted on

Understanding Hermes, Fabric, and the New Architecture in React Native

πŸš€ Understanding Hermes, Fabric, and the New Architecture in React Native

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
Enter fullscreen mode Exit fullscreen mode

🧠 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
Enter fullscreen mode Exit fullscreen mode

πŸ’Ύ 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%)
Enter fullscreen mode Exit fullscreen mode

πŸ”„ Smaller Bundle Size

Bundle Size Impact:
Before Hermes: 41MB APK
After Hermes:  29MB APK (-29%)
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ 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
Enter fullscreen mode Exit fullscreen mode

iOS (ios/Podfile):

use_react_native!(
  :path => config[:reactNativePath],
  # Hermes is now enabled by default
  :hermes_enabled => true,
  :fabric_enabled => flags[:fabric_enabled],
)
Enter fullscreen mode Exit fullscreen mode

⚑ 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]
Enter fullscreen mode Exit fullscreen mode

New JSI Architecture:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ JavaScript  β”‚ ←----------β†’  β”‚    Native    β”‚
β”‚   Thread    β”‚  Direct C++   β”‚   Thread     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   Interface   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       ↓                              ↓
   [Immediate]                  [Immediate]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

πŸ›  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');
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}`);
Enter fullscreen mode Exit fullscreen mode

🎨 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)
Enter fullscreen mode Exit fullscreen mode

Fabric Renderer:

React Components
      ↓
Fabric C++ Core
   ↙     β†˜
iOS UI   Android UI
(Sync)    (Sync)
Enter fullscreen mode Exit fullscreen mode

Key Improvements:

1. Concurrent Rendering Support

// Now works seamlessly with React 18 features
function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <ExpensiveComponent />
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

2. Priority-based Rendering

// High priority updates (user input)
<TextInput onChangeText={setText} />

// Low priority updates (background data)
<LargeList data={backgroundData} />
Enter fullscreen mode Exit fullscreen mode

3. Better Animations

// Fabric enables smoother 60fps animations
const animatedValue = useSharedValue(0);

const animatedStyle = useAnimatedStyle(() => ({
  transform: [{
    translateX: withSpring(animatedValue.value * 100)
  }]
}));
Enter fullscreen mode Exit fullscreen mode

πŸ“Š 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%    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode

🎯 Best Practices

1. Gradual Migration

// Start with non-critical modules
const MyModule = Platform.select({
  ios: NativeModules.MyModuleOld,
  android: TurboModuleRegistry.get<Spec>('MyModule'),
});
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

πŸš€ 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.

Top comments (0)