Codegen is React Native’s code generation system that automatically creates type-safe native interfaces from Flow or TypeScript specifications. It’s a cornerstone of the new architecture, enabling compile-time type safety across the JavaScript/native boundary.
What is Codegen?
Codegen analyzes your JavaScript/TypeScript component and module specs and generates:
C++ interfaces - Abstract base classes for TurboModules and Fabric components
iOS implementations - Objective-C++ glue code
Android implementations - Java/Kotlin JNI bindings
Type definitions - Consistent types across platforms
Located in packages/react-native-codegen/, Codegen consists of:
Parser - Extracts type information from JS/TS files
Schema - Intermediate representation of types
Generators - Platform-specific code generators
Why Codegen?
Before Codegen
Manual native module implementation:
// JavaScript
NativeModules . MyModule . doSomething ( "string" , 42 , callback );
// iOS - manual typing, prone to errors
RCT_EXPORT_METHOD (doSomething:( NSString * )str
num:( NSNumber * )num
callback:(RCTResponseSenderBlock) callback ) {
// Hope the types match!
}
// Android - separate manual typing
@ ReactMethod
public void doSomething ( String str, int num, Callback callback) {
// Hope this matches iOS!
}
Problems:
Type mismatches caught only at runtime
No guarantee iOS and Android match
Boilerplate code for every method
Manual serialization logic
With Codegen
Single source of truth:
// NativeMyModule.ts - SINGLE SPEC
export interface Spec extends TurboModule {
doSomething (
str : string ,
num : number ,
callback : ( result : string ) => void
) : void ;
}
Codegen automatically generates:
C++ abstract interface
iOS Objective-C++ implementation template
Android Java/Kotlin JNI bindings
Type validation at compile time
Codegen ensures type consistency across JavaScript, iOS, Android, and any other platforms. Type errors are caught at build time, not runtime.
Architecture
Schema
Codegen uses an intermediate schema representation defined in src/CodegenSchema.js:
type SchemaType = {
libraryName ?: string ,
modules : {
[ moduleName : string ] : ComponentSchema | NativeModuleSchema
}
};
type NativeModuleSchema = {
type : 'NativeModule' ,
aliasMap : { ... },
spec : {
properties : MethodSchema []
}
};
type MethodSchema = {
name : string ,
optional : boolean ,
typeAnnotation : FunctionTypeAnnotation
};
The schema is platform-agnostic and serves as input to all generators.
Pipeline
┌─────────────────────┐
│ JavaScript/TS │
│ Spec Files │
└──────────┬──────────┘
│
v
┌──────────┴──────────┐
│ Parser │
│ (Flow/TypeScript) │
└──────────┬──────────┘
│
v
┌──────────┴──────────┐
│ Schema │
│ (JSON) │
└──────────┬──────────┘
│
├──────> C++ Generator ──────> .h/.cpp files
│
├──────> iOS Generator ──────> .mm files
│
└──────> Android Generator ──> .java/.kt files
Parsers
Two parsers support different type systems:
Flow Parser (src/parsers/flow/):
// @flow
import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport' ;
export interface Spec extends TurboModule {
+ getValue : () => string ;
}
TypeScript Parser (src/parsers/typescript/):
import { TurboModule } from 'react-native' ;
export interface Spec extends TurboModule {
readonly getValue : () => string ;
}
Both parse to the same schema format.
Generators
Platform-specific code generators:
C++ Generator (src/generators/modules/) - Base TurboModule interfaces
iOS Generator (src/generators/components/) - Objective-C++ implementations
Android Generator (src/generators/modules/) - Java/Kotlin JNI code
Creating a Codegen Spec
TurboModule Spec
Create a spec file with TypeScript:
// NativeCalculator.ts
import { TurboModule , TurboModuleRegistry } from 'react-native' ;
export interface Spec extends TurboModule {
// Synchronous method
add ( a : number , b : number ) : number ;
// Async method
fetchData ( url : string ) : Promise <{ data : string }>;
// Method with callback
process ( input : string , callback : ( result : string ) => void ) : void ;
// Constants
getConstants () : {
PI : number ;
E : number ;
};
}
export default TurboModuleRegistry . getEnforcing < Spec >( 'Calculator' ) ;
Fabric Component Spec
Define a native component:
// MyComponentNativeComponent.ts
import { ViewProps } from 'react-native' ;
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent' ;
import { Int32 , DirectEventHandler } from 'react-native/Libraries/Types/CodegenTypes' ;
interface NativeProps extends ViewProps {
// Primitives
enabled ?: boolean ;
progress ?: number ;
label ?: string ;
// Complex types
colors ?: ReadonlyArray < string >;
style ?: {
borderRadius ?: number ;
padding ?: number ;
};
// Events
onProgress ?: DirectEventHandler <{
value : number ;
}>;
}
export default codegenNativeComponent < NativeProps >( 'MyComponent' ) ;
Commands
Define imperative commands:
import { ViewProps } from 'react-native' ;
import codegenNativeCommands from 'react-native/Libraries/Utilities/codegenNativeCommands' ;
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent' ;
import { Int32 } from 'react-native/Libraries/Types/CodegenTypes' ;
interface NativeProps extends ViewProps {
// Props
}
interface NativeCommands {
scrollTo : ( viewRef : React . ElementRef < typeof NativeComponent >, x : Int32 , y : Int32 ) => void ;
focus : ( viewRef : React . ElementRef < typeof NativeComponent >) => void ;
}
export const Commands = codegenNativeCommands < NativeCommands >({
supportedCommands: [ 'scrollTo' , 'focus' ]
});
const NativeComponent = codegenNativeComponent < NativeProps >( 'MyComponent' );
export default NativeComponent ;
Supported Types
Primitives
interface Spec extends TurboModule {
// Boolean
isEnabled () : boolean ;
// Numbers - all mapped to 'number' in JS
getInt () : number ; // Generates Int32 in native
getDouble () : number ; // Generates double in native
getFloat () : number ; // Generates float in native
// String
getName () : string ;
// Void
doSomething () : void ;
}
Objects
interface Config {
apiKey : string ;
timeout : number ;
retries ?: number ; // Optional
}
interface Spec extends TurboModule {
getConfig () : Config ;
setConfig ( config : Config ) : void ;
}
Codegen generates C++ structs for object types.
Arrays
interface Spec extends TurboModule {
// Array of primitives
getNumbers () : Array < number >;
// Array of objects
getUsers () : Array <{ id : string ; name : string }>;
// Readonly arrays (recommended)
getItems () : ReadonlyArray < string >;
}
Unions (Limited Support)
interface Spec extends TurboModule {
// String unions (enums)
getStatus () : 'idle' | 'loading' | 'success' | 'error' ;
// Number unions
getCode () : 200 | 404 | 500 ;
}
Complex unions (e.g., string | number) are not fully supported. Use object types with type discriminators instead.
Promises
interface Spec extends TurboModule {
// Promise with simple type
fetchData ( url : string ) : Promise < string >;
// Promise with object type
getUser ( id : string ) : Promise <{
id : string ;
name : string ;
email : string ;
}>;
// Promise rejection types are not specified
// Use standard Error in JavaScript
}
Callbacks
interface Spec extends TurboModule {
// Single callback
process ( data : string , callback : ( result : string ) => void ) : void ;
// Success/error callbacks
fetch (
url : string ,
onSuccess : ( data : string ) => void ,
onError : ( error : string ) => void
) : void ;
}
Codegen Type Utilities
import {
Int32 ,
Float ,
Double ,
UnsafeObject ,
WithDefault ,
DirectEventHandler ,
BubblingEventHandler
} from 'react-native/Libraries/Types/CodegenTypes' ;
interface NativeProps extends ViewProps {
// Specific numeric types
count : Int32 ;
opacity : Float ;
latitude : Double ;
// With default value
enabled : WithDefault < boolean , false >;
// Unsafe object (any structure)
metadata ?: UnsafeObject ;
// Event handlers
onPress ?: BubblingEventHandler <{ x : number ; y : number }>;
onLoad ?: DirectEventHandler <{ uri : string }>;
}
Generated Code
C++ Interface
For TurboModule spec:
interface Spec extends TurboModule {
add ( a : number , b : number ) : number ;
}
Codegen generates:
// CalculatorSpec.h
class JSI_EXPORT CalculatorSpec : public TurboModule {
protected:
CalculatorSpec ( std :: shared_ptr < CallInvoker > jsInvoker );
public:
virtual double add ( jsi :: Runtime & rt , double a , double b ) = 0 ;
};
Your implementation:
// Calculator.h
class Calculator : public CalculatorSpec {
public:
Calculator ( std :: shared_ptr < CallInvoker > jsInvoker );
double add ( jsi :: Runtime & rt , double a , double b ) override {
return a + b;
}
};
iOS (Objective-C++)
Codegen generates protocol and base:
// RCTCalculatorSpec.h
@protocol NativeCalculatorSpec < NSObject >
- ( NSNumber * ) add: ( double ) a b: ( double ) b ;
@end
Your implementation:
// RCTCalculator.h
@interface RCTCalculator : NSObject <NativeCalculatorSpec>
@end
// RCTCalculator.mm
@implementation RCTCalculator
RCT_EXPORT_MODULE (Calculator)
- ( NSNumber * ) add: ( double ) a b: ( double ) b {
return @(a + b);
}
- (std::shared_ptr<facebook::react::TurboModule>) getTurboModule :
( const facebook::react::ObjCTurboModule::InitParams & ) params {
return std::make_shared < facebook::react::NativeCalculatorSpecJSI > (params);
}
@end
Android (Java/Kotlin)
Codegen generates abstract class:
// CalculatorSpec.java
public abstract class CalculatorSpec extends NativeTurboModuleSpec {
public CalculatorSpec ( ReactApplicationContext context ) {
super (context);
}
@ ReactMethod ( isBlockingSynchronousMethod = true )
public abstract double add ( double a , double b );
}
Your implementation:
// Calculator.kt
class Calculator (reactContext: ReactApplicationContext )
: CalculatorSpec ( reactContext ) {
override fun getName () = "Calculator"
@ReactMethod (isBlockingSynchronousMethod = true )
override fun add (a: Double , b: Double ): Double {
return a + b
}
}
Running Codegen
Automatic (Recommended)
Codegen runs automatically during build:
# iOS
cd ios && pod install
# Android
cd android && ./gradlew assembleDebug
Codegen output is in:
iOS: ios/build/generated/ios/
Android: android/app/build/generated/source/codegen/
Manual
Run Codegen explicitly:
yarn react-native codegen
Or via Node:
node node_modules/react-native/scripts/generate-codegen-artifacts.js \
--path . \
--outputPath build/codegen
Configuration
Specify Codegen settings in package.json:
{
"name" : "my-app" ,
"codegenConfig" : {
"name" : "MyAppSpec" ,
"type" : "all" ,
"jsSrcsDir" : "./src" ,
"android" : {
"javaPackageName" : "com.myapp.specs"
}
}
}
Options:
name - Library name for generated code
type - "all", "modules", or "components"
jsSrcsDir - Directory containing spec files
android.javaPackageName - Java package for generated Android code
Best Practices
1. Use TypeScript Over Flow
// Recommended: TypeScript has better tooling
import { TurboModule } from 'react-native' ;
export interface Spec extends TurboModule {
getValue () : string ;
}
2. Prefer Readonly Types
// Good: Immutable arrays
getItems (): ReadonlyArray < Item > ;
// Less ideal: Mutable arrays
getItems (): Array < Item > ;
3. Use Explicit Numeric Types for Components
import { Int32 , Float } from 'react-native/Libraries/Types/CodegenTypes' ;
interface NativeProps extends ViewProps {
// Explicit types prevent precision issues
count : Int32 ;
progress : Float ;
}
4. Provide Defaults for Optional Props
import { WithDefault } from 'react-native/Libraries/Types/CodegenTypes' ;
interface NativeProps extends ViewProps {
enabled : WithDefault < boolean , true >;
opacity : WithDefault < Float , 1.0 >;
}
5. Document Complex Types
/**
* Configuration for API client
*/
interface APIConfig {
/**
* Base URL for API requests
*/
baseURL : string ;
/**
* Request timeout in milliseconds
*/
timeout : number ;
}
interface Spec extends TurboModule {
configure ( config : APIConfig ) : void ;
}
// User types
interface User {
id : string ;
name : string ;
email : string ;
}
interface UserPreferences {
theme : 'light' | 'dark' ;
notifications : boolean ;
}
interface Spec extends TurboModule {
getUser ( id : string ) : Promise < User >;
getUserPreferences ( userId : string ) : Promise < UserPreferences >;
updateUserPreferences ( userId : string , prefs : UserPreferences ) : Promise < void >;
}
Troubleshooting
Codegen Not Running
Check:
codegenConfig in package.json is correct
Spec files are in jsSrcsDir
Specs extend TurboModule or use codegenNativeComponent
Force regeneration:
# iOS
cd ios && rm -rf build && pod install
# Android
cd android && ./gradlew clean && ./gradlew assembleDebug
Type Errors
Error: “Unsupported type”
Solution: Ensure you’re using supported types. Complex unions and tuples are not supported.
Error: “Missing required prop”
Solution: Make prop optional with ? or provide default with WithDefault.
Generated Code Not Found
Check output directories:
# iOS
find ios/build/generated -name "*Spec.h"
# Android
find android/app/build/generated/source/codegen -name "*.java"
If empty, Codegen didn’t run. Check build logs.
Mismatched Types
Error: “Cannot convert between types”
Solution: Ensure JavaScript types match native implementation:
// Spec says Promise<string>
fetchData (): Promise < string > ;
// Implementation must return NSString via promise
- ( void )fetchData:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
resolve ( @"string" ); // ✓ Correct
// resolve(@123); // ✗ Wrong type!
}
Advanced Topics
Custom Codegen Types
Extend Codegen with custom parsers:
// my-custom-parser.js
module . exports = {
parseFile ( filename , code ) {
// Custom parsing logic
return schema ;
}
};
Generate code for custom platforms:
// my-platform-generator.js
function generate ( schema ) {
// Generate code for custom platform
return {
'MyModule.cpp' : cppCode ,
'MyModule.h' : headerCode
};
}
Schema Validation
Validate schemas before generation:
const { SchemaValidator } = require ( '@react-native/codegen' );
const errors = SchemaValidator . validate ( schema );
if ( errors . length > 0 ) {
console . error ( 'Schema validation failed:' , errors );
}
Migration Guide
From Legacy Native Modules
Before:
import { NativeModules } from 'react-native' ;
const { MyModule } = NativeModules ;
After:
// 1. Create spec
import { TurboModule , TurboModuleRegistry } from 'react-native' ;
export interface Spec extends TurboModule {
getValue () : string ;
}
export default TurboModuleRegistry . getEnforcing < Spec >( 'MyModule' ) ;
// 2. Update usage
import NativeMyModule from './specs/NativeMyModule' ;
const value = NativeMyModule . getValue ();
From Manual Type Definitions
Before:
// Manual types, might drift from implementation
interface MyModuleType {
getValue () : string ;
}
declare const MyModule : MyModuleType ;
After:
// Codegen ensures types match implementation
export interface Spec extends TurboModule {
getValue () : string ;
}
Further Reading
TurboModules See Codegen in action with TurboModules
Fabric Renderer Component specs for Fabric
Architecture Overview How Codegen fits in the architecture
JavaScript Interface JSI types used by Codegen