Skip to main content

Structure of the React Codebase

· 6 min read
Sanjeev Sarda
High Performance Developer

React is all around us, but more specifically it's a monorepo containing several packages. Here's a breakdown on the structure of the core React codebase, also covers the React Scheduler and Reconciler.

React

React Codebase Structure

The structure of the codebase has separation of concerns between the core logic, the reconciliation algorithm, and the platform-specific renderers.

Package Breakdown

packages/

This directory contains the source code for all React packages.

  • react: The core package. It contains the isomorphic APIs like React.createElement, React.Component, React.useState, etc. It is platform-agnostic.
    • Note: An "isomorphic API" (or universal API) means the code can run in multiple environments (Web, Mobile, VR) without modification. It defines how you describe a UI, while renderers handle how to draw it.
  • react-reconciler: This is the heart of React. It implements the "Fiber" architecture and the reconciliation algorithm. It manages the component tree, diffing, and state updates. It is used by renderers to implement custom rendering logic.
  • scheduler: A package responsible for cooperative multitasking. It handles prioritizing tasks (like rendering updates) to ensure the main thread remains responsive.
  • react-dom: The renderer for the DOM. It acts as the bridge between react-reconciler and the browser's DOM APIs.
  • react-native-renderer: The renderer for React Native.
  • shared: Contains utility functions and constants shared across multiple packages.

fixtures/

Contains small React applications used for testing and debugging.

scripts/

Contains scripts for building, testing, and managing the repository (e.g., release scripts, flow checks).

Architectural Layers

  1. Core (API): react package. What developers interact with.
  2. Reconciler (Logic): react-reconciler. Handles the "when" and "what" of updates.
  3. Renderer (Platform): react-dom, react-native-renderer. Handles the "where" (painting pixels, DOM nodes).

React Scheduler

The scheduler package is a standalone package in the React repository responsible for scheduling tasks. It implements a cooperative multitasking model.

JavaScript is single-threaded. If a long-running task (like rendering a large component tree) blocks the main thread, the browser becomes unresponsive to user input, animations stutter, and the app feels sluggish.

The Scheduler allows React to break long tasks into smaller chunks (time slicing) and yield control back to the browser periodically.

Key Concepts

Cooperative Multitasking

Instead of the browser deciding when to stop a task (preemptive), the task itself (React) cooperatively yields control back to the Scheduler. The Scheduler then decides whether to continue the task or yield to the browser to handle higher-priority events (like input or painting).

Time Slicing

The Scheduler allocates a "slice" of time (typically 5ms) for React to do work.

  • React performs work.
  • It periodically checks Scheduler.shouldYield().
  • If the time slice is expired, React pauses its work and yields.
  • The Scheduler schedules a continuation callback to resume the work in the next available frame.

Priorities

The Scheduler assigns priorities to tasks to determine their execution order.

  • ImmediatePriority: Must run synchronously (e.g., expired tasks).
  • UserBlockingPriority: Result of user interaction (e.g., button click). Needs to run quickly (within ~250ms) to feel instant.
  • NormalPriority: Default priority for state updates (e.g., data fetch response). (Target ~5s).
  • LowPriority: Low priority work (e.g., logging). (Target ~10s).
  • IdlePriority: Run only when the main thread is completely idle.

Implementation Details

  • MessageChannel: The Scheduler primarily uses MessageChannel to schedule tasks on the macro-task queue. This is preferred over setTimeout because it has less nesting clamping and runs earlier in the event loop.
  • requestHostCallback: The internal mechanism to request the host (browser) to execute a callback.
  • shouldYieldToHost: A function that checks if the current deadline has been exceeded or if there is high-priority input pending (using navigator.scheduling.isInputPending if available).

Integration with React

The react-reconciler depends on the scheduler.

  1. When a state update occurs, React asks the Scheduler to schedule a callback.
  2. The Scheduler invokes the callback when appropriate based on priority.
  3. Inside the callback, React performs the "Render Phase".
  4. During the render loop, React checks shouldYield() to see if it needs to pause.

React Reconciler and Fiber Architecture

The react-reconciler package is the core logic engine of React. It is responsible for computing changes in the UI component tree and applying them efficiently. Since React 16, this has been powered by the Fiber architecture.

What is Fiber?

Fiber is a reimplementation of the stack, specialized for React components. You can think of a single fiber as a virtual stack frame.

The primary goal of Fiber is to enable incremental rendering: the ability to split rendering work into chunks and spread it out over multiple frames.

Key Features

  1. Pause and Resume: React can pause work on a low-priority update to handle a high-priority event (like user input), and then resume where it left off.
  2. Prioritization: Different types of updates have different priorities (e.g., animation > user input > data fetching).
  3. Reuse: React can reuse previously completed work if it's still valid.
  4. Abort: React can throw away work if it's no longer needed.

The Structure of a Fiber

A Fiber is a JavaScript object that contains information about a component, its input, and its output. Key properties include:

  • type: The function or class associated with the fiber (e.g., div, MyComponent).
  • key: Unique identifier for the fiber, used for diffing lists.
  • child, sibling, return: Pointers that form the fiber tree structure (a singly linked list traversal of the tree).
  • pendingProps / memoizedProps: The props used to create the fiber vs the props used during the last render.
  • memoizedState: The internal state of the fiber (e.g., hooks list).
  • alternate: A link to the counterpart fiber in the "other" tree (see Double Buffering).

Double Buffering

React maintains two fiber trees at any given time:

  1. Current Tree: Represents what is currently displayed on the screen.
  2. Work-in-Progress (WIP) Tree: The tree React builds in the background during an update.

When React starts working on an update, it clones the current tree to create the workInProgress tree. It performs all logic and diffing on the WIP tree. Once the WIP tree is complete and committed to the DOM, it becomes the new current tree.

The Two Phases

The reconciliation process is divided into two phases:

1. Render Phase (Asynchronous, Interruptible)

  • React traverses the fiber tree, calling component functions and diffing children.
  • It calculates what changes need to be made (side effects).
  • This phase is pure (no side effects visible to the user) and can be paused, aborted, or restarted.
  • Result: A tree of fibers marked with "flags" (effects) describing operations like Placement, Update, or Deletion.

2. Commit Phase (Synchronous, Uninterruptible)

  • React takes the finished WIP tree and applies the calculated changes to the host environment (e.g., the DOM).
  • Lifecycle methods like componentDidMount and useEffect are triggered here.
  • This phase cannot be interrupted because it results in visible changes to the UI.

https://github.com/facebook/react