skip to content

Fabric View Components

A Fabric native component wraps a platform view behind a codegenNativeComponent spec with typed props, events and commands. Interviewers ask when a custom view beats a module.

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

explore

questions

6

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%

answer

  1. one has no UI at all
  2. host component in the React tree
  3. props in, events out, commands aside
  4. laid out by Yoga like View
  5. drawing needs a native view

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.

solid answer

~40 s

A **Turbo Module** exposes native functions — `getItem`, `unlock`, `getStepsSinceBoot` — obtained from `TurboModuleRegistry` and called like an API; it renders nothing. A **Fabric native component** is declared with `codegenNativeComponent<NativeProps>('SignaturePad')`, rendered as JSX like `<View>`, laid out by Yoga with the rest of the tree, and backed by a real platform view: a `SimpleViewManager`-managed Android `View`, or an `RCTViewComponentView` subclass on iOS. Data flows in through **typed props**, out through **events** (`DirectEventHandler` or `BubblingEventHandler`), and imperative calls such as `clear()` go through **commands** from `codegenNativeCommands`. A proof-of-delivery signature pad must sit in the layout and draw strokes natively as the finger moves, so it is a component; a module could only act on it from outside.

code

typescript · 18 lines
typescript
// specs/SignaturePadNativeComponent.ts
import type {CodegenTypes, ColorValue, HostComponent, ViewProps} from 'react-native';
import {codegenNativeComponent} from 'react-native';

type StrokeEndEvent = {
  pointCount: CodegenTypes.Int32;
};

export interface NativeProps extends ViewProps {
  strokeColor?: ColorValue;
  strokeWidth?: CodegenTypes.WithDefault<CodegenTypes.Float, 3>;
  disabled?: CodegenTypes.WithDefault<boolean, false>;
  onStrokeEnd?: CodegenTypes.DirectEventHandler<StrokeEndEvent> | null;
}

export default codegenNativeComponent<NativeProps>(
  'SignaturePad',
) as HostComponent<NativeProps>;

go deeper

for a junior

Remember that modules are functions without UI and components are views in the React tree, with props, events and commands.

for a middle

Map each capability to its mechanism, from codegenNativeComponent and ViewProps layout to event handler types, commands and the native base classes on each platform.

for a senior

Decide between a component, a module, composition or an existing library for a real feature, and split responsibilities, such as a pad component plus an upload module.

for a principal

Weigh owning native UI on two platforms against library adoption or a JavaScript implementation, considering upgrade cost, team skills and latency needs.

## Two ways to reach native code React Native's New Architecture offers two typed extension points, both described by a spec that **Codegen** turns into native glue: - A **Turbo Module** is a native object whose **methods** JavaScript calls. Its spec extends `TurboModule` and is loaded with `TurboModuleRegistry.getEnforcing`. It has no position on screen. - A **Fabric native component** is a **host component** — an element that React renders directly to a platform view, the way `View` and `Text` are. Its spec is a file ending in `NativeComponent.ts` that calls `codegenNativeComponent<NativeProps>('SignaturePad')`. ## What a component has that a module does not | Capability | Turbo Module | Fabric native component | |---|---|---| | Appears in the view hierarchy | No | Yes, one native view per rendered element | | Laid out by Yoga (flexbox, `style`) | No | Yes, `NativeProps` extends `ViewProps` | | Input from JavaScript | Method arguments | Typed **props** | | Output to JavaScript | Return values, promises, module events | Component **events** (`DirectEventHandler`, `BubblingEventHandler`) | | Imperative calls | Every method is one | **Commands** from `codegenNativeCommands` | | Lifetime | One instance for the React instance | One view per mounted element | | Native base on Android | Generated abstract spec class | `SimpleViewManager` with a generated delegate | | Native base on iOS | Class adopting the generated spec protocol | `RCTViewComponentView` subclass | ## Why a signature pad is a component A parcel-delivery app's proof-of-delivery screen shows a box where the customer signs with a finger. Consider what that box must do: 1. **Occupy layout space** next to the recipient's name and the confirm button — only a view laid out by Yoga can do that. 2. **Receive touches and draw strokes** as the finger moves, which is smoothest when the platform view handles its own touch stream and drawing. 3. **Be configured declaratively** — stroke colour, stroke width, a disabled state once the parcel is confirmed — which is exactly what props are for. 4. **Report back** when a stroke ends or the pad becomes non-empty, which is what component events are for. 5. **Accept occasional imperative requests** — `clear()` when the customer taps "Start again" — which is what commands are for. A Turbo Module fits none of the first three: it has no view to occupy space, receive touches or be styled. It is the right tool beside the component, for example a module that uploads the exported signature image, but not for the pad itself. ## When not to write either - **Composition may be enough.** If core components and a JavaScript drawing approach meet the latency and quality bar, there is no native code to maintain. - **An existing library may exist.** Adopting a maintained component is often cheaper than owning native code on two platforms. - **Expo projects have another route.** The Expo Modules API offers its own view definitions; that is a separate authoring model with its own contract. ## How the pieces are named For a spec `SignaturePadNativeComponent.ts` calling `codegenNativeComponent<NativeProps>('SignaturePad')`, the string `'SignaturePad'` is the component's name everywhere: the Android view manager's `getName()` returns it, the iOS registration in `codegenConfig.ios.componentProvider` maps it to a class, and Codegen derives the generated interface, delegate and props types from it. The file must end in `NativeComponent` — Codegen uses that suffix to detect component specs. Everything the spec imports — `codegenNativeComponent`, the `CodegenTypes` namespace, `HostComponent`, `ViewProps`, `ColorValue` — comes from the root `react-native` entry point; in React Native 0.87 deep imports such as `react-native/Libraries/Types/CodegenTypes`, which older guides show, are a type error under the default Strict TypeScript API. ## Mistakes interviewers listen for - Calling a native component a "native module with UI" and trying to return a view from a module method. - Believing a native component is a WebView or an image of native UI — it is the real platform view in the hierarchy. - Using a module to draw on a view it cannot own, instead of giving the drawing surface its own component. - Forgetting that each rendered element is a separate native view with its own state.

  • Could a Turbo Module draw on an existing React Native View instead of writing a component?
    Not in a way React Native supports. A module has no view of its own, and the views React Native mounts belong to the renderer, which reapplies props and may recycle them. Drawing belongs in a component that owns its platform view. A module fits work beside it, such as uploading the exported signature.
  • How does data get out of a signature pad component when the courier taps Confirm?
    Through the component's own channels: a command such as `exportSignature()` asks the native view to render its strokes, and an event such as `onSignatureExported` carries the result, for example a file path, back to JavaScript. Commands return nothing, so the answer arrives as an event.

A Turbo Module is a phone line to a back office: you call, ask for something and get an answer, but nothing appears in your shop window. A native component is a display case built into the window itself: it takes up space in the layout, customers see it and touch it, and you change what it shows by rearranging its contents (props).

saying these in an interview costs you the question

  • A native component is a Turbo Module whose methods return views
  • Native components render as images or web views of native UI
  • A Turbo Module can be placed in JSX if it has a render method
  • Props for a native component are passed as method arguments at mount time
  • Each native component type has a single shared native view instance
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

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

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