Skip to main content
Fabric is React Native’s reimagined rendering system, built from the ground up to leverage JSI and support modern React features like Concurrent React. It replaces the legacy UIManager with a type-safe, performant renderer that can execute layout synchronously on any thread.

Core Concepts

Shadow Tree

The shadow tree is an immutable tree of shadow nodes that represents the structure and properties of your UI. This tree exists in C++ and serves as the single source of truth for the UI state.

Shadow Nodes

Shadow nodes are immutable objects defined in ReactCommon/react/renderer/core/ShadowNode.h:
class ShadowNode : public Sealable, 
                   public DebugStringConvertible, 
                   public jsi::NativeState {
 public:
  // Core shadow node data
  std::shared_ptr<const ShadowNodeFamily> family_;
  std::shared_ptr<const Props> props_;
  std::shared_ptr<const EventEmitter> eventEmitter_;
  std::shared_ptr<const State> state_;
  std::shared_ptr<const std::vector<std::shared_ptr<const ShadowNode>>> children_;
};
Key properties:
  • Immutability - Once created, shadow nodes never change; mutations create new nodes
  • Structural sharing - Unchanged subtrees are shared between versions
  • Layout cache - Yoga layout results are cached on nodes
  • Type safety - Strongly typed props specific to each component type

Shadow Node Family

Each shadow node belongs to a “family” that identifies the component instance across revisions:
  • Maintains component identity through re-renders
  • Enables React’s reconciliation across shadow tree updates
  • Links shadow nodes to their JavaScript component instances

Component Descriptors

Component descriptors are factories for creating and cloning shadow nodes of specific types. They’re defined in ReactCommon/react/renderer/core/ComponentDescriptor.h. Each native component (View, Text, Image, etc.) has a descriptor that:
  • Creates shadow nodes with the correct concrete type
  • Parses and creates typed Props objects
  • Manages component-specific State
  • Handles event emission
The ComponentDescriptorRegistry maintains all available descriptors and looks them up by component name.

Props

Props are immutable, type-safe property bags that configure shadow nodes:
// Example: ViewProps.h
struct ViewProps : public AccessibilityProps {
  Float opacity{1.0};
  Color backgroundColor{};
  BorderMetrics borderMetrics{};
  YGStyle yogaStyle{};
  Transform transform{};
  // ... many more properties
};
Props are:
  • Strongly typed - Each component has its own Props struct
  • Codegen-generated - For custom components, Codegen creates Props definitions
  • Efficient - Only changed props are serialized across the JSI boundary
  • Platform-specific - Can include platform-specific properties

State

State represents native-managed state for a component, separate from React state:
template <typename DataT>
class ConcreteState final : public State {
 public:
  using Shared = std::shared_ptr<const ConcreteState>;
  using Data = DataT;
};
Common uses:
  • Image loading state - Track image load progress
  • Text layout - Cache measured text dimensions
  • Scroll position - Native scroll state
  • Native animations - Animation progress
State updates trigger shadow node cloning without requiring React re-renders.

Event Emitters

Event emitters handle communication from native back to JavaScript:
class EventEmitter {
public:
  void dispatchEvent(
    std::string eventName,
    folly::dynamic &&payload,
    EventPriority priority = EventPriority::AsynchronousBatched
  ) const;
};
Event priorities:
  • Discrete - High priority events like press, focus (sync to next frame)
  • Continuous - Scroll, drag, mouse move (batched and throttled)
  • AsynchronousBatched - Default, batched with event loop

Rendering Pipeline

Fabric’s rendering pipeline consists of three main phases:

1. Render Phase

Executes on the JavaScript thread:
React Component Tree

   React Renders

Calls to UIManager (via JSI)

Creates/Clones Shadow Nodes

   New Shadow Tree
React calls methods on the UIManagerBinding (exposed to JavaScript via JSI):
  • createNode() - Create new shadow nodes
  • cloneNode() - Clone nodes with updated props
  • appendChild() - Build tree structure
  • completeRoot() - Commit the tree
All these operations are synchronous JSI calls - no bridge serialization.

2. Commit Phase

Executes on background thread, coordinated by the ShadowTree:
New Shadow Tree (from React)

  Commit Hooks (pre-commit)

  Layout Calculation (Yoga)

  Commit Hooks (post-commit)

  Tree Diffing

 Mounting Transaction

Layout Calculation

Yoga (Facebook’s flexbox implementation) computes layout:
  • Runs on any thread (typically background)
  • Cached results reused for unchanged subtrees
  • Supports measure functions for text and images
  • Returns pixel-perfect layouts

Commit Hooks

Commit hooks allow intercepting the commit process:
  • Pre-commit hooks - Modify tree before layout (e.g., Animation Backend)
  • Post-commit hooks - Process results after layout
Definitions in ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h.

Diffing

The Differentiator compares old and new shadow trees:
  • Generates minimal set of mutations
  • Handles insertions, deletions, updates, reordering
  • Optimized for common patterns (append, prepend)
  • Output is a MountingTransaction

3. Mount Phase

Executes on the UI thread:
Mounting Transaction

  Mount Hooks (pre-mount)

 Apply Mutations to Views
   (Create, Update, Delete)

  Mount Hooks (post-mount)

  Views Visible to User
The MountingCoordinator delivers transactions to the platform:
class MountingCoordinator {
public:
  MountingTransaction pullTransaction() const;
};
Platform mounting implementations:
  • iOS - RCTMountingManager applies mutations to UIViews
  • Android - MountingManager applies mutations to Android Views

UIManager

The UIManager orchestrates the entire rendering system. Defined in ReactCommon/react/renderer/uimanager/UIManager.h:
class UIManager final : public ShadowTreeDelegate {
public:
  UIManager(const RuntimeExecutor &runtimeExecutor,
            std::shared_ptr<const ContextContainer> contextContainer);
  
  // Shadow tree management
  void startSurface(ShadowTree::Unique &&shadowTree, ...);
  ShadowTree::Unique stopSurface(SurfaceId surfaceId);
  
  // Node operations (called from JavaScript)
  std::shared_ptr<ShadowNode> createNode(Tag tag, ...);
  std::shared_ptr<ShadowNode> cloneNode(const ShadowNode &shadowNode, ...);
  void appendChild(const ShadowNode &parent, const ShadowNode &child);
  
  // Commit coordination
  void shadowTreeWillCommit(...);
  void shadowTreeDidFinishTransaction(...);
};
The UIManager:
  • Manages multiple shadow trees (one per React root/surface)
  • Coordinates commits across shadow trees
  • Applies mutations via delegates
  • Integrates with animation backend
  • Manages component descriptor registry

Surface Management

A “surface” is a React root rendered into a native view hierarchy. Each surface:
  • Has its own ShadowTree
  • Has a unique SurfaceId
  • Can be mounted/unmounted independently
  • Supports different display modes (visible, suspended, hidden)
void UIManager::startSurface(
  ShadowTree::Unique &&shadowTree,
  const std::string &moduleName,
  const folly::dynamic &props,
  DisplayMode displayMode
);
Multiple surfaces enable:
  • Native navigation with React screens
  • Modal overlays
  • Native UI with embedded React content

Synchronous Layout

One of Fabric’s major improvements is synchronous layout measurement:
// JavaScript can measure layout synchronously!
const node = ref.current;
const {width, height} = node.measureLayout();
// Use dimensions immediately, no callback needed
This is possible because:
  • Layout is calculated in C++, accessible to JavaScript via JSI
  • No asynchronous bridge round-trip
  • Cached results returned instantly
Use cases:
  • Positioning tooltips and popovers
  • Measuring text before render
  • Implementing custom layouts
  • Avoiding layout flicker

Runtime Shadow Node Reference Updates (RSNRU)

RSNRU is an optimization that keeps React’s references to shadow nodes fresh. From ReactCommon/react/renderer/core/__docs__/RSNRU.md:
RSNRU ensures React always holds references to the latest shadow node revisions, even after they’re cloned internally by Fabric operations like layout or state updates.
Benefits:
  • Better layout cache hits - React reuses computed layouts
  • Fresh native state - State updates visible to React
  • Fewer re-layouts - Avoids redundant layout calculations
How it works:
  1. Shadow nodes can be cloned by native operations (layout, state updates)
  2. When cloned, RSNRU updates wrapper objects held by React
  3. React’s next render uses the latest revision
  4. Layout cache and state remain fresh
Enabled by default in single-threaded mode.

Concurrent React Support

Fabric is designed for Concurrent React features:

Priority-Based Rendering

Different updates have different priorities:
  • Synchronous - User input, critical updates
  • Default - Most state updates
  • Idle - Analytics, logging
The RuntimeScheduler processes tasks by priority:
void RuntimeScheduler::scheduleTask(
  SchedulerPriority priority,
  jsi::Function callback
);

Interruptible Rendering

Low-priority work can be interrupted:
  • Start rendering a low-priority update
  • High-priority event occurs (user input)
  • Pause low-priority work
  • Process high-priority update immediately
  • Resume or discard low-priority work

Time Slicing

Long renders are broken into chunks:
  • Render for 5ms
  • Yield to handle input
  • Resume rendering
  • Prevents blocking user interactions

Platform Integration

iOS (Objective-C++)

// RCTFabricSurface.mm
@interface RCTFabricSurface : UIView
- (instancetype)initWithSurfacePresenter:(RCTSurfacePresenter *)surfacePresenter
                              moduleName:(NSString *)moduleName
                       initialProperties:(NSDictionary *)initialProperties;
@end
Key iOS components:
  • RCTSurfacePresenter - Manages surfaces and mounting
  • RCTMountingManager - Applies mutations to UIViews
  • RCTScheduler - Coordinates React renders
  • RCTComponentViewRegistry - Maps tags to UIViews

Android (JNI/C++)

// FabricUIManager.java
public class FabricUIManager implements UIManager {
  private native void registerBinding(long jsContext);
  private native void startSurface(int surfaceId, String moduleName, ...);
  // ... native methods
}
Key Android components:
  • FabricUIManager - Java interface to C++ UIManager
  • MountingManager - Applies mutations to Android Views
  • ReactInstanceManager - Manages React instances
  • ViewManagerRegistry - Component view managers

Performance Characteristics

Fabric delivers significant performance improvements:

Startup Performance

  • Faster initialization - C++ components initialized once
  • Lazy mounting - Views created only when needed
  • Shared layout engine - Yoga shared across components

Runtime Performance

  • Synchronous operations - No bridge serialization delays
  • Efficient diffing - Minimal mutation sets
  • Layout cache - Computed layouts reused extensively
  • Priority scheduling - Important updates processed first

Memory Usage

  • Structural sharing - Unchanged subtrees shared
  • Copy-on-write - Only changed nodes allocated
  • Smart pointers - Automatic memory management

Debugging Fabric

Enable Fabric Logs

iOS:
// AppDelegate.mm
RCTSetLogThreshold(RCTLogLevelInfo);
Android:
// MainApplication.java
ReactNativeFeatureFlags.useFabricInterop(true);

Inspector Integration

Fabric integrates with React DevTools:
  • Inspect shadow tree structure
  • View props and state
  • Measure layout
  • Track renders and commits

Performance Profiling

Use native profilers:
  • Xcode Instruments - Time Profiler, Allocations
  • Android Studio Profiler - CPU, Memory, Frame rendering
  • React DevTools Profiler - Component render times

Migration from Legacy Renderer

Legacy and Fabric renderers cannot render the same component tree simultaneously. Migration must be per-surface.

Enabling Fabric

React Native 0.68+ uses Fabric by default. To ensure it’s enabled: iOS Podfile:
use_react_native!(
  :path => config[:reactNativePath],
  :fabric_enabled => true
)
Android gradle.properties:
newArchEnabled=true

Component Compatibility

Most built-in components work with Fabric. Custom native components need:
  • Component descriptors - Define in C++
  • Codegen specs - TypeScript/Flow component spec
  • View managers - Update for Fabric APIs

Breaking Changes

  • setNativeProps removed - Use state updates instead
  • findNodeHandle deprecated - Use refs directly
  • UIManager methods changed - Some APIs removed or modified
  • Timing differences - Synchronous operations may change timing

Best Practices

Optimize Re-renders

Minimize shadow tree churn:
// Good: Stable props
<View style={styles.container}>

// Bad: New object every render
<View style={{flex: 1}}>

Use React.memo

Prevent unnecessary component updates:
const MyComponent = React.memo(({data}) => {
  return <View>...</View>;
});

Batch State Updates

Multiple updates in same task are batched:
// These create a single commit
setState1(value1);
setState2(value2);
setState3(value3);

Avoid Layout Thrashing

Don’t interleave layout reads and writes:
// Bad: Read-write-read-write
const h1 = ref1.measureLayout().height;
setHeight1(h1);
const h2 = ref2.measureLayout().height;
setHeight2(h2);

// Good: Batch reads, then writes
const h1 = ref1.measureLayout().height;
const h2 = ref2.measureLayout().height;
setHeight1(h1);
setHeight2(h2);

Further Reading

Threading Model

Understand Fabric’s threading architecture

TurboModules

Learn about native modules in new architecture

JavaScript Interface

JSI fundamentals powering Fabric

Codegen

Generate component specs

Build docs developers (and LLMs) love