React Native’s New Architecture replaces the asynchronous bridge with JSI — a C++ layer that lets JavaScript call native code synchronously, without serialization overhead. TurboModules are JSI-based native modules with TypeScript spec files that Codegen compiles to type-safe C++ interfaces. Fabric is the new renderer that supports concurrent React features like useTransition and startTransition. Bridgeless mode eliminates the legacy bridge entirely. React Native 0.74+ enables the New Architecture by default. Reanimated 3 runs animations entirely on the UI thread via worklets, eliminating frame drops from bridge latency. Claude Code generates TurboModule specs, Fabric component specs, Codegen configurations, Reanimated 3 animated components, and the native module wiring for production React Native New Architecture apps.
CLAUDE.md for React Native New Architecture
## React Native New Architecture Stack
- Version: react-native >= 0.74 (New Arch enabled by default)
- Bridgeless: enabled in RN 0.74+ — no legacy bridge, pure JSI
- TurboModules: define in NativeXxx.ts spec file, implement in C++/ObjC/Java
- Fabric: native components defined in NativeXxxComponent spec
- Codegen: runs during build — generates C++ headers from TypeScript specs
- Reanimated: >= 3.x for worklets, useAnimatedStyle, useSharedValue
- Animations: useSharedValue + useAnimatedStyle replaces Animated.Value
TurboModule Spec
// src/specs/NativeOrderModule.ts — TurboModule TypeScript spec
import type { TurboModule } from "react-native"
import { TurboModuleRegistry } from "react-native"
// Codegen generates native interface from this spec
export interface Spec extends TurboModule {
// All methods must be explicitly typed
getOrders(customerId: string): Promise<OrderInfo[]>
createOrder(payload: CreateOrderPayload): Promise<string> // Returns orderId
cancelOrder(orderId: string): Promise<void>
getStoredAuthToken(): string | null // Synchronous — JSI enables this
}
interface OrderInfo {
id: string
status: string
totalCents: number
createdAt: string
}
interface CreateOrderPayload {
customerId: string
items: OrderItem[]
}
interface OrderItem {
productId: string
quantity: number
priceCents: number
}
// Register with Codegen name
export default TurboModuleRegistry.getEnforcing<Spec>("OrderModule")
// ios/OrderModule/NativeOrderModule.mm — iOS TurboModule implementation
#import "NativeOrderModule.h"
#import <React/RCTUtils.h>
@implementation NativeOrderModule
RCT_EXPORT_MODULE()
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params {
return std::make_shared<facebook::react::NativeOrderModuleSpecJSI>(params);
}
// Synchronous method — runs on JS thread via JSI
- (NSString *)getStoredAuthToken {
return [[NSUserDefaults standardUserDefaults] stringForKey:@"auth_token"];
}
// Async methods use the provided resolve/reject
- (void)getOrders:(NSString *)customerId
resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Network call off main thread
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://api.example.com/orders?customer=%@", customerId]];
NSData *data = [NSData dataWithContentsOfURL:url];
if (!data) {
reject(@"FETCH_ERROR", @"Failed to fetch orders", nil);
return;
}
NSError *error = nil;
NSArray *orders = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
if (error) {
reject(@"PARSE_ERROR", error.localizedDescription, error);
} else {
resolve(orders);
}
});
}
@end
Usage in React Components
// src/hooks/useOrders.ts — consuming TurboModule
import NativeOrderModule from "../specs/NativeOrderModule"
import { useEffect, useState, useCallback } from "react"
export function useOrders(customerId: string) {
const [orders, setOrders] = useState<OrderInfo[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<Error | null>(null)
const fetchOrders = useCallback(async () => {
try {
setLoading(true)
// Direct JSI call — no bridge serialization
const data = await NativeOrderModule.getOrders(customerId)
setOrders(data)
} catch (e) {
setError(e as Error)
} finally {
setLoading(false)
}
}, [customerId])
useEffect(() => {
fetchOrders()
}, [fetchOrders])
// Synchronous call — works because of JSI
const authToken = NativeOrderModule.getStoredAuthToken()
const cancelOrder = useCallback(async (orderId: string) => {
await NativeOrderModule.cancelOrder(orderId)
setOrders(prev => prev.filter(o => o.id !== orderId))
}, [])
return { orders, loading, error, cancelOrder, authToken, refetch: fetchOrders }
}
Reanimated 3 Animations
// src/components/OrderCard.tsx — Reanimated 3 with New Arch
import React from "react"
import { Pressable, Text, View, StyleSheet } from "react-native"
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
withTiming,
interpolateColor,
runOnJS,
} from "react-native-reanimated"
import { Gesture, GestureDetector } from "react-native-gesture-handler"
interface OrderCardProps {
order: OrderInfo
onCancel: (id: string) => void
}
export function OrderCard({ order, onCancel }: OrderCardProps) {
// Shared values — live on UI thread, no JS bridge
const scale = useSharedValue(1)
const translateX = useSharedValue(0)
const opacity = useSharedValue(1)
const isPressed = useSharedValue(false)
// Animated styles — computed on UI thread
const cardStyle = useAnimatedStyle(() => ({
transform: [
{ scale: scale.value },
{ translateX: translateX.value },
],
opacity: opacity.value,
backgroundColor: interpolateColor(
isPressed.value ? 1 : 0,
[0, 1],
["#ffffff", "#f0f0f0"]
),
}))
// Gesture runs on UI thread via worklet
const panGesture = Gesture.Pan()
.onUpdate((event) => {
"worklet"
translateX.value = event.translationX
})
.onEnd((event) => {
"worklet"
if (event.translationX < -100) {
// Swipe left to cancel
translateX.value = withTiming(-500, { duration: 200 })
opacity.value = withTiming(0, { duration: 200 }, (finished) => {
if (finished) {
runOnJS(onCancel)(order.id)
}
})
} else {
translateX.value = withSpring(0)
}
})
const tapGesture = Gesture.Tap()
.onBegin(() => {
"worklet"
isPressed.value = true
scale.value = withSpring(0.97)
})
.onFinalize(() => {
"worklet"
isPressed.value = false
scale.value = withSpring(1)
})
const composed = Gesture.Simultaneous(tapGesture, panGesture)
return (
<GestureDetector gesture={composed}>
<Animated.View style={[styles.card, cardStyle]}>
<Text style={styles.orderId}>#{order.id.slice(-6)}</Text>
<Text style={styles.status}>{order.status}</Text>
<Text style={styles.total}>
${(order.totalCents / 100).toFixed(2)}
</Text>
<Text style={styles.hint}>← Swipe to cancel</Text>
</Animated.View>
</GestureDetector>
)
}
const styles = StyleSheet.create({
card: {
padding: 16,
borderRadius: 12,
marginVertical: 6,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
orderId: { fontFamily: "monospace", fontSize: 14 },
status: { color: "#666", marginTop: 4 },
total: { fontSize: 18, fontWeight: "bold", marginTop: 4 },
hint: { fontSize: 11, color: "#999", marginTop: 4 },
})
Fabric Native Component
// src/specs/NativeOrderMapComponent.ts — Fabric component spec
import type { ViewProps } from "react-native"
import type { HostComponent } from "react-native"
import codegenNativeComponent from "react-native/Libraries/Utilities/codegenNativeComponent"
interface NativeProps extends ViewProps {
// All props must be primitive or registered types
orders: ReadonlyArray<Readonly<{
id: string
latitude: number
longitude: number
status: string
}>>
selectedOrderId?: string
onOrderSelected?: (event: { nativeEvent: { orderId: string } }) => void
showClusters?: boolean
}
export default codegenNativeComponent<NativeProps>(
"OrderMapComponent"
) as HostComponent<NativeProps>
app.json New Architecture Config
{
"name": "OrderApp",
"expo": {
"newArchEnabled": true,
"android": {
"gradle": {
"newArchEnabled": true
}
},
"ios": {
"newArchEnabled": true
}
}
}
# ios/Podfile — enable New Architecture
require_relative '../node_modules/react-native/scripts/react_native_pods'
platform :ios, min_ios_version_supported
prepare_react_native_project!
target 'OrderApp' do
config = use_native_modules!
use_react_native!(
:path => config[:reactNativePath],
:app_path => "#{Pod::Config.instance.installation_root}/..",
# Enables New Architecture (Fabric + TurboModules)
:hermes_enabled => true,
)
end
For the Expo managed workflow that abstracts TurboModule setup and provides a curated set of New Architecture-compatible modules, see the Expo guide for managed vs bare workflow decisions. For React Native performance profiling beyond animations — measuring JS thread and UI thread frame rates, identifying layout thrashing — the mobile performance guide covers Flashlight and Perfetto. The Claude Skills 360 bundle includes React Native New Architecture skill sets covering TurboModule specs, Reanimated 3, and Codegen configuration. Start with the free tier to try native module generation.