React Native New Architecture: 5 Core Patterns for Fabric Renderer and TurboModules
React Native New Architecture: The Three Pillars of Performance Leap
The old architecture's async Bridge serialization overhead, layout calculation jank, and limited native module extensibility — React Native's performance bottlenecks have long been criticized. The new architecture achieves synchronous JSI calls, concurrent rendering, and type-safe bridging through Fabric renderer, TurboModules, and Codegen. In 2026, React Native new architecture is the default, and migration is no longer optional.
This article covers 5 core patterns, guiding you through Fabric rendering → TurboModules → Codegen → migration strategy → performance optimization.
Core Concepts
| Concept | Description |
|---|---|
| Fabric | New renderer replacing old UI Manager |
| TurboModules | New native module system replacing NativeModules |
| JSI | JavaScript Interface, C++ synchronous call layer |
| Codegen | Auto-generates C++/Java/ObjC bridging code |
| Shadow Tree | Fabric's virtual tree supporting concurrent computation |
| Yoga | Cross-platform layout engine |
| Event Pipeline | New event system with priority scheduling |
| State Update | Fabric state updates with batch commits |
Problem Analysis: 5 Major New Architecture Migration Challenges
- C++ knowledge barrier: TurboModules and Fabric require C++ code
- Legacy library compatibility: Many third-party libraries not yet adapted
- Complex Codegen configuration: Type specs and code generation learning curve
- Changed debugging experience: New architecture debugging differs from old
- Difficult progressive migration: Issues with mixed old/new architecture runtime
Step-by-Step: 5 New Architecture Implementation Patterns
Pattern 1: TurboModule Native Module
// NativeCalculator.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
add(a: number, b: number): Promise<number>;
multiply(a: number, b: number): number;
getConstants: () => {
PI: number;
VERSION: string;
};
}
export default TurboModuleRegistry.getEnforcing<Spec>('Calculator');
// CalculatorModule.java
@ReactModule(name = CalculatorModule.NAME)
public class CalculatorModule extends NativeCalculatorSpec implements TurboModule {
public static final String NAME = "Calculator";
@Override
public double add(double a, double b) {
return a + b;
}
@Override
public double multiply(double a, double b) {
return a * b;
}
@Override
public Map<String, Object> getTypedExportedConstants() {
return Map.of("PI", Math.PI, "VERSION", "2.0.0");
}
}
Pattern 2: Fabric Custom Component
// CustomText.ts
import type { HostComponent } from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
export interface NativeProps {
text: string;
fontSize?: number;
color?: string;
maxLines?: number;
}
export default codegenNativeComponent<NativeProps>('CustomText');
// CustomTextManager.java
public class CustomTextManager extends ViewGroupManager<TextView>
implements CustomTextManagerInterface<TextView> {
private final CustomTextManagerDelegate mDelegate = new CustomTextManagerDelegate(this);
@Override
public String getName() { return "CustomText"; }
@Override
protected TextView createViewInstance(ThemedReactContext reactContext) {
return new TextView(reactContext);
}
@Override
public void setText(TextView view, String text) { view.setText(text); }
@Override
public void setFontSize(TextView view, float fontSize) { view.setTextSize(fontSize); }
@Override
public CustomTextManagerDelegate getDelegate() { return mDelegate; }
}
Pattern 3: Codegen Configuration and Type Specs
// package.json
{
"codegenConfig": {
"name": "MyAppSpec",
"type": "modules",
"jsSrcsDir": "src/specs",
"android": { "javaPackageName": "com.example.specs" }
}
}
// src/specs/NativeStorage.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
getItem(key: string): Promise<string | null>;
setItem(key: string, value: string): Promise<void>;
removeItem(key: string): Promise<void>;
getAllKeys(): Promise<string[]>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('Storage');
Pattern 4: Event Emission and Priority Scheduling
// CustomViewManager.java
private void emitOnPress(ReactContext reactContext, int viewId) {
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
viewId, "onPress", Arguments.createMap()
);
}
private void dispatchEventWithPriority(ThemedReactContext context, int viewId) {
EventDispatcher dispatcher = context.getNativeModule(UIManagerModule.class)
.getEventDispatcher();
dispatcher.dispatchEvent(new CustomPressEvent(viewId));
}
Pattern 5: Progressive Migration and Interop Layer
// app.json
{ "expo": { "newArchEnabled": true } }
// Check if new architecture is enabled
const isNewArch = global.__turboModuleProxy != null;
console.log('New Architecture:', isNewArch);
// Compatible with both architectures
const isTurbo = global.__turboModuleProxy != null;
const MyModule = isTurbo
? TurboModuleRegistry.getEnforcing<Spec>('MyModule')
: NativeModules.MyModule;
Pitfall Guide
Pitfall 1: TurboModule return type mismatch
// ❌ Wrong: Codegen spec doesn't match implementation
export interface Spec extends TurboModule {
getData(): string; // declared sync
}
// Java: returns Promise (actually async)
// ✅ Correct: keep consistent
export interface Spec extends TurboModule {
getData(): Promise<string>; // async declaration
}
Pitfall 2: Fabric component not registered
// ❌ Wrong: created Manager but didn't register
public class CustomTextManager extends ViewGroupManager<TextView> { ... }
// ✅ Correct: register in Package
public class MyAppPackage extends BaseReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return List.of(new CustomTextManager());
}
}
Pitfall 3: C++ compilation errors
// ❌ Wrong: missing JSI header reference
#include <jsi/jsi.h> // path may be incorrect
// ✅ Correct: configure CMake properly
// CMakeLists.txt
target_include_directories(mymodule PRIVATE
${REACT_NATIVE_DIR}/ReactCommon/jsi
)
Pitfall 4: Ignoring thread safety
// ❌ Wrong: operating JS values outside JS thread
void callback(jsi::Runtime& rt) {
std::thread([&rt]() {
rt.global().setProperty(rt, "result", 42); // crash!
}).detach();
}
// ✅ Correct: schedule back to JS thread via JSI dispatcher
void callback(jsi::Runtime& rt) {
std::thread([this]() {
runOnJSQueue([this](jsi::Runtime& rt) {
rt.global().setProperty(rt, "result", 42);
});
}).detach();
}
Pitfall 5: Not handling old architecture fallback
// ❌ Wrong: assuming new architecture is always available
const module = TurboModuleRegistry.getEnforcing<Spec>('MyModule');
// ✅ Correct: compatible with both architectures
const isTurbo = global.__turboModuleProxy != null;
const MyModule = isTurbo
? TurboModuleRegistry.getEnforcing<Spec>('MyModule')
: NativeModules.MyModule;
Error Troubleshooting
| # | Error | Cause | Solution |
|---|---|---|---|
| 1 | TurboModuleRegistry: MyModule could not be found |
Native module not registered | Check Package registration and module name |
| 2 | Codegen error: type mismatch |
TS spec doesn't match implementation | Unify Codegen type definitions and native implementation |
| 3 | C++ compilation error: jsi.h not found |
Header file path error | Configure CMake include paths |
| 4 | ViewManager not found |
Fabric component not registered | Register ViewManager in Package |
| 5 | Invariant Violation: new arch |
New architecture not enabled | Set newArchEnabled=true |
| 6 | Event delivery failed |
Event name mismatch | Check event name matches registration |
| 7 | JSI binding failed |
C++ binding initialization failed | Check onLoad and install functions |
| 8 | Shadow node creation failed |
Yoga layout calculation error | Check custom layout properties |
| 9 | Interop layer crash |
Legacy module bridging crash | Upgrade legacy module or use compatibility layer |
| 10 | Build failed: NDK not found |
Android NDK not configured | Install NDK and configure ANDROID_NDK |
Advanced Optimization
- JSI direct ArrayBuffer manipulation: Zero-copy binary data transfer for image/audio processing
- Fabric Concurrent Layout: Enable concurrent layout computation to reduce main thread blocking
- Lazy TurboModule loading: Load native modules on demand to reduce startup time
- Custom ShadowNode: Implement complex layouts like waterfall, virtual lists
- C++ state sharing: Share C++ state via JSI, avoiding serialization overhead
Comparison
| Dimension | New Arch (Fabric) | Old Arch (Bridge) | Flutter | KMP Compose |
|---|---|---|---|---|
| Rendering Performance | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| JS-Native Communication | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| Type Safety | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Ecosystem Compatibility | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ |
| Learning Curve | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| Cross-Platform Consistency | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
Summary: React Native new architecture fundamentally solves old architecture performance bottlenecks through Fabric, TurboModules, and Codegen. The new architecture suits React Native projects seeking native performance, especially interaction-intensive apps. With the new architecture as default in 2026, migrate early for continued performance improvements and ecosystem support.
Online Tools
- JSON Formatter: /en/json/format
- Hash Calculator: /en/encode/hash
- cURL to Code: /en/dev/curl-to-code
Try these browser-local tools — no sign-up required →