skip to content

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%

answer

  1. the module's registered name
  2. legacy: hand-written, became NativeModules.<name>
  3. generated NAME constant
  4. copied from getEnforcing's argument
  5. Turbo lookup goes through the package

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.

solid answer

~40 s

`NativeModule.getName()` is documented as the name JavaScript uses to require the module. In the legacy system it really was the registration key: every hand-written `ReactContextBaseJavaModule` implemented it, `ReactPackage.createNativeModules()` returned instances, and React Native registered each under `getName()` (or an `@ReactModule` name), exposing it as `NativeModules.<name>`. With Turbo Modules, Codegen writes an abstract spec such as `NativeStepCounterSpec` with `NAME = "NativeStepCounter"` — copied from the spec's `TurboModuleRegistry.getEnforcing` call — and a `getName()` returning it, so your Kotlin class inherits a correct value. In React Native 0.87, a `BaseReactPackage` module is resolved by the requested name through the package's `ReactModuleInfo` map and `getModule`; keep all of them, and `getName()`, on the one generated constant.

code

kotlin · 13 lines
kotlin
import com.facebook.react.bridge.ReactApplicationContext
import com.walkchallenge.specs.NativeStepCounterSpec

// Generated NativeStepCounterSpec already declares
//   NAME = "NativeStepCounter" and getName() = NAME
class StepCounterModule(reactContext: ReactApplicationContext) :
    NativeStepCounterSpec(reactContext) {

  override fun isAvailable(): Boolean = true
}

// In the package, reuse the generated constant instead of a second copy:
// if (name == NativeStepCounterSpec.NAME) StepCounterModule(reactContext) else null

go deeper

for a junior

Remember that getName() is the module's name, that legacy JavaScript read it as NativeModules.<name>, and that a Turbo Module's generated spec implements it from the getEnforcing string.

for a middle

Trace the name from getEnforcing to NAME, the ReactModuleInfo key and the getModule check, and explain why legacy registration used getName() while Turbo lookup goes through the package.

for a senior

Debug a could-not-be-found error by comparing where the name lives, and remove hand-written constants that can drift from the generated one.

for a principal

Set a naming convention for in-house modules, such as a Native prefix and a single generated constant, so a rename touches one place on each platform.

## What getName() is On Android, every **native module** — a Kotlin or Java class that JavaScript can call — implements the `NativeModule` interface, and its first method is `getName()`. The interface documents the return value as *the name used to require this module from JavaScript*. It is the module's **identity**: not a display label and not the Kotlin class name. How much React Native relies on it depends on how the module is registered, which is why the answer differs between the legacy system and Turbo Modules. ## The legacy system: getName() was the registration key Before the New Architecture, a module extended `ReactContextBaseJavaModule`, marked callable methods with `@ReactMethod`, and **had to** implement `getName()` — the legacy docs say so in as many words. Registration went like this: 1. A `ReactPackage` returned instantiated modules from `createNativeModules()`. 2. React Native read each module's name — from an `@ReactModule(name = ...)` annotation if present, otherwise from `getName()` — and registered it under that name. 3. JavaScript reached it as `NativeModules.StepCounter`. So in that world, a typo in `getName()` directly broke the JavaScript lookup. React Native 0.87's interop layer still registers legacy packages exactly this way. ## Turbo Modules: the name comes from the spec With Turbo Modules the name is declared once, in the typed JavaScript spec, and **Codegen** propagates it. For a spec file `NativeStepCounter.ts` whose default export is `TurboModuleRegistry.getEnforcing<Spec>('NativeStepCounter')`, the generated Java class `NativeStepCounterSpec` contains: - `public static final String NAME = "NativeStepCounter";` — copied from the `getEnforcing` argument; - a constructor taking a `ReactApplicationContext`; - an override of `getName()` that returns `NAME`. A Kotlin module extending `NativeStepCounterSpec` therefore already has the right `getName()`. The class name comes from the **spec file name**; `NAME` comes from the **`getEnforcing` string** — two inputs that usually look alike but are independent. Because `NAME` is `public static final`, Kotlin code refers to it as `NativeStepCounterSpec.NAME`. For an app, the generated class is written under `android/app/build/generated/source/codegen`, in the Java package named by `codegenConfig.android.javaPackageName` (Codegen falls back to `com.facebook.fbreact.specs` when none is given), so it is build output to reference, never a file to edit or commit. ## How a Turbo Module is actually found A Turbo Module lives in a `BaseReactPackage`. When JavaScript asks for `'NativeStepCounter'`, React Native checks the package's `getReactModuleInfoProvider()` map for that name with `isTurboModule = true`, then calls the package's `getModule(name, reactContext)` with it. The lookup matches the **requested name against the package**, not against the instance's `getName()`. | Place the name appears | Role in lookup | |---|---| | `getEnforcing('NativeStepCounter')` in the spec | The name JavaScript requests | | Key of the package's `ReactModuleInfo` map | Must match, or `getModule` is never called | | `if (name == ...)` inside `getModule` | Must match, or no instance is returned | | Generated `NAME` and `getName()` | The module's self-reported identity; used by legacy-style registration and in React Native's own warnings | When the first three disagree, JavaScript fails with `TurboModuleRegistry.getEnforcing(...): 'NativeStepCounter' could not be found`. ## Why the tutorial still overrides it React Native's Android tutorial writes `override fun getName() = NAME` with a hand-written `companion object { const val NAME = "NativeLocalStorage" }`. That compiles — the generated method is not final — and gives the package a constant to reference. The cost is a **second copy** of the name: if it drifts from the generated one, logs and warnings name a module JavaScript never asks for, and any code that registers the class the legacy way uses the wrong key. Referencing the generated `NativeStepCounterSpec.NAME` everywhere avoids that. ## Mistakes to avoid - **Treating `getName()` as the class name** — `StepCounterModule` is the class; `NativeStepCounter` is the name. - **Believing Turbo Modules dropped `getName()`** — `NativeModule` still requires it; the generated spec implements it for you. - **Renaming in one place** — a new `getEnforcing` string must reach the info map, `getModule` and any hand-written constant together. - **Reusing a core module's name** — replacing an existing module is an explicit opt-in on the package's module info, not something to trigger by accident.

  • Which two inputs name the generated class and its NAME constant?
    The class name comes from the spec file: `NativeStepCounter.ts` produces `NativeStepCounterSpec`. The `NAME` constant, and therefore the generated `getName()`, comes from the string passed to `TurboModuleRegistry.getEnforcing` in that file. They often match by convention, but renaming one does not rename the other.
  • For a legacy module in an old-style ReactPackage, what name does React Native register it under?
    The interop code reads an `@ReactModule(name = ...)` annotation on the class if there is one, and otherwise calls `getName()` on the instance returned from `createNativeModules()`. That is why a wrong `getName()` broke legacy modules outright, while a Turbo Module in a `BaseReactPackage` is keyed by its `ReactModuleInfo` entry.

saying these in an interview costs you the question

  • getName() returns the Kotlin class name, so renaming the class renames the module
  • Turbo Modules no longer have a getName() method at all
  • The spec file name, not the getEnforcing string, decides the module's NAME
  • A BaseReactPackage Turbo Module is looked up by calling getName() on every instance
  • Two modules may share a name and React Native merges their methods