skip to content

Turbo Modules & Codegen

Writing your own Turbo Modules and Fabric components: a typed spec, Codegen's generated interfaces, iOS and Android code, and publishing. Interviewers use it to see if you have shipped native code.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

30

In React Native, what does autolinking do when you install a library that contains native code, and what must you still do yourself?

level: juniorimportance: must knowfreq 50%

answer

  1. runs at native build time
  2. reads the project config command's output
  3. Podfile and settings.gradle hooks
  4. pod install, then a native rebuild
  5. a JS reload cannot add native code

basics

~20 s

Autolinking finds the app's dependencies that ship native code and adds them to the iOS and Android builds automatically. You still run pod install on iOS, rebuild the native app, and do any library-specific setup such as permissions.

solid answer

~40 s

When the native build runs, autolinking asks for the project's configuration (by default `npx @react-native-community/cli config`; in Expo projects `expo-modules-autolinking` supplies it) and gets the list of `package.json` dependencies that contain native code. On iOS the Podfile's `use_native_modules!` adds each library's podspec as a pod; on Android `autolinkLibrariesFromCommand()` in `settings.gradle` includes each library's Gradle project and the app build registers their packages. That replaced manual linking, where you dragged projects into Xcode and edited Gradle files by hand. What remains yours: run `pod install` after adding or removing a native library, rebuild the binary (a Metro reload or an over-the-air JS update cannot add native code), and follow any extra setup the library documents, such as Info.plist keys or Android permissions.

code

bash · 4 lines
bash
npm install my-org-pdf-viewer
cd ios && bundle exec pod install && cd ..
npm run ios
npm run android

go deeper

for a junior

Recall that autolinking adds native libraries at build time, and that you must run pod install and rebuild the app before the module exists.

for a middle

Explain the config command, the Podfile and Gradle hooks, the generated package list and what react-native.config.js can override.

for a senior

Show how you read the config output to debug a missing module, handle transitive native dependencies and keep Expo and community autolinking behaviour straight.

for a principal

Set a team rule for adding native dependencies: review cost, binary size, rebuild and release implications, and who owns each library's upgrade path.

## The problem autolinking solves Many React Native libraries are part JavaScript and part **native code**: an Objective-C, Swift, Kotlin, Java or C++ implementation compiled into the app binary. Installing the package with npm or Yarn only puts files into `node_modules`; the iOS and Android build systems know nothing about them. Before React Native 0.60, developers **linked** each library by hand: dragging an Xcode project into the app, editing `settings.gradle` and `build.gradle`, and registering the package in Java code. **Autolinking** automates all of that. ## How it works 1. **Discovery.** At native build time, a config command reports the project's dependencies. By default it is `npx @react-native-community/cli config`; Expo projects use `expo-modules-autolinking`, which has replaced community autolinking by default since SDK 52. The command looks at the dependencies declared in the app's `package.json` and keeps the ones that ship native code for a platform. 2. **iOS.** The Podfile calls `use_native_modules!`, which runs the config command, then adds each library's **podspec** as a CocoaPods pod. A package with no podspec is skipped with a warning. 3. **Android.** `settings.gradle` calls `autolinkLibrariesFromCommand()`, which includes each library's Gradle project; the app's `build.gradle` calls `autolinkLibrariesWithApp()`, which adds them as dependencies and generates the package list that registers them at startup. The config output is cached and re-run only when `package.json`, a lockfile or `react-native.config.js` changes. 4. **Codegen.** Libraries that declare a `codegenConfig` get their Turbo Module and Fabric glue generated as part of the same build. ## What you still do yourself | Step | Why | |---|---| | `pod install` (iOS) after adding or removing a native library | CocoaPods must regenerate the workspace to include the new pod | | Rebuild and reinstall the app | Native code only enters through a native build | | Library-specific setup | Info.plist usage strings, Android permissions or an Expo config plugin are not autolinking's job | | In an Expo project, a new development build | The installed binary does not contain the new native code | The most common beginner mistake is installing a native library, reloading Metro, and seeing an error that the native module could not be found. The JavaScript arrived; the native half did not, because the binary on the device was built before the install. ## Controlling autolinking An optional **`react-native.config.js`** at the app root adjusts the result. The most common use is disabling a library on one platform: ```javascript module.exports = { dependencies: { 'some-native-library': { platforms: {android: null}, }, }, }; ``` A **library** can ship its own `react-native.config.js` too, describing where its native sources live when they are not in the default `android/` and `ios/` folders, or how to register a pure C++ module. ## Autolinking in Expo projects Expo projects use **Expo Autolinking** (`expo-modules-autolinking`), which links both Expo modules and React Native modules. It differs from the community CLI in a few ways worth knowing: - It resolves the app's dependencies **recursively**, following the Node.js resolution algorithm, so a native library that arrives through another package is still found (since SDK 54). - It links **local modules** from a `./modules` directory by default (`nativeModulesDir`), and extra locations listed in `searchPaths`. - Options such as `exclude` live under `expo.autolinking` in `package.json`. - `npx expo-modules-autolinking verify` warns about duplicate installations of a native module. ## What autolinking does not do - It does not install anything; npm or Yarn does. - It does not follow arbitrary transitive dependencies with the community CLI: a native library your library depends on must also be in the app's own `package.json` (Expo Autolinking resolves transitive dependencies). - It does not make a library compatible with your React Native version; that is the library's peer-dependency range. - It does not update the binary on users' phones; only a store release or a new build does.

  • How do you see what autolinking will link?
    Run the config command yourself: `npx @react-native-community/cli config` prints JSON with a `dependencies` object, and each entry shows the platforms it links on, such as the podspec path for iOS and the source directory for Android. In an Expo project, `npx expo-modules-autolinking react-native-config` prints the same format.
  • Why does Android pick up a new library without an extra command?
    The React Native Gradle plugin re-runs the config command whenever `package.json`, a lockfile or `react-native.config.js` has changed, then includes the discovered library projects. You still need a native rebuild, but no separate step like `pod install`.

Autolinking is like a building's electrician wiring every appliance you bring in during the next scheduled rewiring: plugging in a new appliance between rewirings does nothing until the electrician runs the next build.

saying these in an interview costs you the question

  • After npm install, a Metro reload is enough to use a native library
  • You still need to drag the library's Xcode project into the app
  • Autolinking links every package found anywhere in node_modules
  • Autolinking also adds the library's permissions and Info.plist keys
  • An over-the-air JS update can deliver a newly installed native library
open as a page

In React Native, what is the difference between a Turbo Module and a Fabric native component, and which does a signature-pad view need?

level: juniorimportance: must knowfreq 45%

basics

~20 s

A Turbo Module is a set of native functions with no UI, called from JavaScript. A Fabric native component is a platform view in the React tree, configured by props, reporting through events, driven by commands. A signature pad draws on screen, so it needs a component.

open as a page

In React Native 0.87, how do you implement an Android Turbo Module in Kotlin against its Codegen-generated abstract spec class?

level: middleimportance: must knowfreq 45%

basics

~20 s

Subclass the generated abstract NativeXxxSpec, passing the ReactApplicationContext to its constructor, and override every abstract method it declares, with a Promise parameter for async results and a plain return for sync ones. Then return an instance from a BaseReactPackage.

open as a page

When designing a React Native Turbo Module spec, when should a method return a Promise, take a callback, or be an EventEmitter instead?

level: middleimportance: must knowfreq 50%

basics

~20 s

Return a Promise for one asynchronous result that can fail, declare an EventEmitter for many values over time, and treat a callback as a one-shot legacy alternative; a plain return value suits only cheap, immediate data.

open as a page

In React Native 0.87, what must an iOS Turbo Module's Objective-C++ class implement to satisfy its Codegen-generated spec?

level: middleimportance: must knowfreq 45%

basics

~20 s

The class adopts the generated NativeXxxSpec protocol, implements every selector it declares, returns the generated NativeXxxSpecJSI from getTurboModule:, returns the JavaScript name from +moduleName, and is registered in codegenConfig.ios so React Native can map that name to the class.

open as a page

What rules must a React Native Turbo Native Module spec file such as NativeBatteryHealth.ts follow for Codegen to accept it?

level: middleimportance: must knowfreq 50%

basics

~10 s

Name the file Native<Something>.ts, declare exactly one interface extending TurboModule named Spec, and load it with exactly one typed TurboModuleRegistry.getEnforcing<Spec>() or get<Spec>() call whose argument is a string literal of letters, digits and underscores.

open as a page

In React Native on Android, what is a native module's getName() for, and where does its value come from in a Turbo Module?

level: juniorimportance: should knowfreq 35%

basics

~20 s

getName() returns the name a native module is known by. Legacy packages registered modules under it, as NativeModules.<name>. In a Turbo Module the Codegen-generated spec implements it from the getEnforcing name, while lookup itself runs through the package's ReactModuleInfo key and getModule.

open as a page

In React Native 0.87, how does a component subscribe to a Turbo Module event declared with CodegenTypes.EventEmitter, and how does it stop listening?

level: juniorimportance: should knowfreq 30%

basics

~10 s

Call the spec's emitter property as a function with a handler, for example NativeHeartRate.onHeartRate(handler); it returns an EventSubscription, and calling its remove() in the effect cleanup stops the handler when the screen unmounts.

open as a page

In React Native on iOS, why must a Turbo Module's implementation file be Objective-C++ (.mm) rather than plain Objective-C (.m)?

level: juniorimportance: should knowfreq 30%

basics

~20 s

The Codegen spec is partly C++: getTurboModule: must return a std::shared_ptr to a generated C++ JSI class, and typed object parameters arrive as C++ structs. Only an Objective-C++ (.mm) file compiles C++ and Objective-C together.

open as a page

In React Native, what is a Turbo Native Module spec, and what does Codegen generate from it?

level: juniorimportance: should knowfreq 45%

basics

~20 s

A Turbo Native Module spec is a TypeScript or Flow file, such as NativeBatteryHealth.ts, whose Spec interface declares the module's methods. At build time Codegen turns it into C++ glue, a Java spec class and an Objective-C++ protocol for native code to implement.

open as a page

In React Native on Android, what do BaseReactPackage's getModule and getReactModuleInfoProvider each contribute when registering a Turbo Module?

level: middleimportance: should knowfreq 30%

basics

~20 s

getReactModuleInfoProvider advertises which module names the package can supply and how to treat them (isTurboModule, needsEagerInit, canOverrideExistingModule). getModule is the lazy factory React Native calls with one of those names to create the instance the first time JavaScript asks.

open as a page

When migrating a legacy React Native Android module built on ReactContextBaseJavaModule and @ReactMethod to a Turbo Module, what changes and what stays the same?

level: middleimportance: should knowfreq 35%

basics

~20 s

The module stops extending ReactContextBaseJavaModule directly and extends the generated spec, which itself extends it and carries the @ReactMethod annotations. Registration moves from an eager ReactPackage.createNativeModules list to a lazy BaseReactPackage with getModule and a module-info map.

open as a page

In a React Native Turbo Module spec, how do TypeScript types such as number, boolean, object literals and Promise map to Kotlin and Objective-C?

level: middleimportance: should knowfreq 30%

basics

~20 s

Codegen maps number to double, boolean to a native boolean, string to String or NSString, object literals to ReadableMap on Android and a generated struct on iOS, and a Promise return to extra native arguments that settle it.

open as a page

When migrating a legacy React Native iOS module built on RCT_EXPORT_MODULE and RCT_EXPORT_METHOD to a Turbo Module, what replaces each macro?

level: middleimportance: should knowfreq 35%

basics

~20 s

RCT_EXPORT_MODULE's two jobs split: you write +moduleName yourself and register the class under codegenConfig.ios in package.json. RCT_EXPORT_METHOD disappears: the TypeScript spec declares each method and the class implements the matching selector from the generated protocol.

open as a page

Why should a React Native library, such as an in-house PDF viewer, declare react-native as a peerDependency rather than a regular dependency?

level: middleimportance: should knowfreq 35%

basics

~20 s

An app must contain exactly one react-native and one react. A peerDependency tells the package manager to use the app's copy and to warn when versions are incompatible, instead of installing a second copy inside the library.

open as a page

In a React Native Turbo Native Module spec, what is the difference between TurboModuleRegistry.getEnforcing and TurboModuleRegistry.get, and when should you use get?

level: middleimportance: should knowfreq 38%

basics

~20 s

getEnforcing returns the module or throws an invariant error saying it could not be found in the native binary; get returns null instead. Use get only when the module may legitimately be missing, and null-check it at every call site.

open as a page

How do you expose an imperative clear() on a React Native Fabric component with codegenNativeCommands, and how does each platform receive it?

level: middleimportance: should knowfreq 25%

basics

~10 s

Declare a NativeCommands interface whose methods take the component ref first, export Commands = codegenNativeCommands<NativeCommands>({supportedCommands: ['clear']}) from the spec, and call Commands.clear(ref.current). Codegen routes it to the Android manager's clear(view) and iOS handleCommand:args:.

open as a page

In a React Native Fabric component spec, how do DirectEventHandler and BubblingEventHandler differ, and which suits a signature pad's onStrokeEnd?

level: middleimportance: should knowfreq 25%

basics

~20 s

A direct event is delivered only to the component that emitted it. A bubbling event goes through capture and bubble phases, so ancestors registered for it, including onXCapture handlers, can see it too. A signature pad's onStrokeEnd concerns only the pad, so DirectEventHandler fits.

open as a page

Wrapping Android's step-counter sensor in a React Native Turbo Module, how do you use ReactApplicationContext safely across activity changes, backgrounding and JavaScript reloads?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Treat ReactApplicationContext as an Application-scoped handle: use it for system services, read currentActivity only when needed and never store it, pause the sensor from a LifecycleEventListener, and unregister everything in invalidate(), which runs before the React instance is torn down.

open as a page

In a React Native 0.87 Turbo Module, on which thread do synchronous-return, void and Promise methods run, and why does that matter in production?

level: seniorimportance: should knowfreq 35%

basics

~20 s

A method that returns a value runs synchronously on the JS thread and blocks it; void and Promise methods run later on a background native-modules queue. None of them runs on the main thread, so UI work needs explicit dispatch.

open as a page

In a React Native iOS Turbo Module, how should UIKit work reach the main thread, and what do requiresMainQueueSetup and methodQueue change?

level: seniorimportance: should knowfreq 25%

basics

~20 s

Asynchronous Turbo Module methods arrive on a background method queue, so UIKit calls must be dispatched to the main queue inside the method. +requiresMainQueueSetup only decides where the module is constructed; overriding methodQueue to force every call onto main is deprecated.

open as a page

In React Native on iOS, how do you wrap a vendor's Swift-only smart-lock SDK as a Turbo Module, and how do you split the work between Swift and Objective-C++?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Use an adapter: a Swift class that calls the vendor SDK and exposes an Objective-C-compatible surface, plus a thin Objective-C++ class that adopts the Codegen spec, owns the adapter and forwards each call, because Swift cannot implement the C++-bearing spec directly.

open as a page

After adding an in-house React Native PDF-viewer library, TurboModuleRegistry.getEnforcing throws that 'NativePdfViewer' could not be found; how do you diagnose whether autolinking is to blame?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Confirm the binary was rebuilt after install, then print the autolinking config: if the library is missing, fix discovery; if it is listed, check the native module is registered under the exact spec name.

open as a page

When does React Native's Codegen run on Android and on iOS, and why can a newly added or edited spec still leave the native build missing its generated interface?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Codegen runs during native builds: on Android through the generateCodegenArtifactsFromSchema Gradle task, on iOS during pod install and a CocoaPods build phase. A spec is missed when misnamed or outside jsSrcsDir, or, on iOS, added without re-running pod install.

open as a page

Implementing a React Native Fabric component natively, what do SimpleViewManager with a generated ViewManagerDelegate on Android and an RCTViewComponentView subclass on iOS each require?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Android needs a SimpleViewManager implementing the generated XxxManagerInterface, returning the generated delegate from getDelegate(), registered in a package. iOS needs an Objective-C++ RCTViewComponentView subclass that diffs props in updateProps:oldProps:, returns its component descriptor and is mapped in codegenConfig.ios.

open as a page

Three React Native apps need an in-app PDF viewer; how do you decide between adopting a community native library and building and publishing an in-house one?

level: principalimportance: should knowfreq 25%

basics

~20 s

Adopt a community library if it supports the New Architecture, meets your requirements, fits Expo and bare apps and is maintained; build in-house only when requirements or risk justify owning two native codebases through every upgrade.

open as a page

In React Native, what is a pure C++ Turbo Module, and when would a library choose it over separate Kotlin and Objective-C implementations?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

A pure C++ Turbo Module implements a typed spec once in C++ and runs on both Android and iOS. Choose it for platform-independent logic or an existing C++ library; use Kotlin and Objective-C when the work needs platform SDKs.

open as a page

In a React Native package.json, what do codegenConfig's name, type, jsSrcsDir and android.javaPackageName fields control?

level: middleimportance: nice to knowfreq 25%

basics

~20 s

codegenConfig tells Codegen what to generate: name labels the generated C++ and Objective-C++ files, type selects modules, components or all, jsSrcsDir is the folder searched for specs, and android.javaPackageName sets the package of the generated Java spec classes.

open as a page

In a React Native codegenNativeComponent spec, what does CodegenTypes.WithDefault do, and what do other props default to natively?

level: middleimportance: nice to knowfreq 20%

basics

~10 s

WithDefault<Type, Value> sets the native default of an optional prop, used when JavaScript omits or removes it. Props without it get Codegen's type default: 0 for numbers, false for booleans, null for strings.

open as a page

A React Native Fabric signature pad on iOS sometimes appears already filled with a previous delivery's strokes on a new screen; why, and how do you fix it?

level: seniorimportance: nice to knowfreq 15%

basics

~20 s

iOS Fabric recycles component views: an unmounted RCTViewComponentView is pooled per component type and reused by a later mount. Strokes held in the view, not in props, survive. Reset them in prepareForRecycle, or opt the class out of recycling.

open as a page