How React Works Under the Hood

React may seem magical, but behind the scenes, it's a highly optimized and elegant system.

In this comprehensive deep dive, we’ll explore:

  • What React DOM really does
  • How JSX is parsed into JavaScript objects
  • Why every component is just a function or object
  • The diffing and reconciliation process
  • How Hooks like useState and useEffect really work
  • Optimization strategies React uses
  • React Fiber: the heart of the React rendering engine
  • How to build your own mini React

Let’s decode the internals

1. React DOM: The Rendering Engine

React is a powerful JavaScript library for building user interfaces, but it doesn't interact directly with the browser's Document Object Model (DOM). That crucial responsibility is handled by ReactDOM, which serves as the rendering engine that connects React's declarative component model to the actual browser environment. When developers write a command like ReactDOM.render(, document.getElementById('root'));, they're instructing ReactDOM to take the virtual component tree and mount it into the specified location in the real DOM. Behind the scenes, ReactDOM creates a virtual representation of the UI elements, compares them during updates using a diffing algorithm, and applies only the minimal set of changes needed to the real DOM — improving performance and responsiveness.

ReactDOM's strength lies in its ability to efficiently manage host environment logic, meaning it knows how to interact with browser-specific APIs like document.createElement, appendChild, or removeChild. This separation of concerns allows React itself to remain platform-agnostic — enabling other renderers like React Native or React 360 — while ReactDOM is specifically tailored for web browsers. By handling DOM interactions, event delegation, and mounting lifecycle logic, ReactDOM plays a critical role in making React applications feel fast, interactive, and modern.

2. How JSX Works Internally

JSX may look like regular HTML, but it's actually a JavaScript syntax extension that simplifies how we write React components. Instead of manually calling JavaScript functions to create DOM elements, JSX allows us to write components in a familiar, HTML-like structure. But here's the catch: JSX doesn’t directly work in the browser. It’s converted into JavaScript before the browser can run it. JSX is essentially syntactic sugar, meaning it makes writing code easier, but it still gets converted into something else under the hood.

JSX to react component
Let’s break it down with a simple example:

Hello, world!

At first glance, this looks like regular HTML. But when JSX is compiled by tools like Babel, it gets turned into a JavaScript function call like this:

React.createElement('div', null, 'Hello, world!');

This may seem confusing at first, but what’s happening here is that React.createElement is a function that React uses to create a virtual representation of the UI you’re building. In this case, it’s creating an object that describes the

Why Do We Use JSX?

JSX makes React development more intuitive and less verbose. Imagine creating a list of items in pure JavaScript without JSX:

const list = React.createElement('ul', null, 
  React.createElement('li', null, 'Item 1'),
  React.createElement('li', null, 'Item 2'),
  React.createElement('li', null, 'Item 3')
);

This looks cumbersome compared to the JSX version:

  • Item 1
  • Item 2
  • Item 3

As you can see, JSX provides a cleaner, more readable way to write React components. And under the hood, it’s just a simpler, more intuitive way to call React.createElement.

3. Components Are Objects or Functions

In React, components are the building blocks of your application. A component is a reusable piece of code that defines part of the UI. You can think of a component as a function or a class, both of which return a description of what the UI should look like. This description is what React will use to create the actual user interface in the browser. So, when you create components, you’re basically writing a function or class that returns what the UI should look like at that moment.

Function Components
Let’s start with function components, which are simpler and more common in modern React development .A function component is just a function that returns JSX. Here’s an example:

function MyButton() {
  return ;
}

In this example, MyButton is a function that returns a JSX element a

Now, when you call the MyButton component in JSX like this:

React doesn’t directly render it to the page. Instead, it takes the JSX and creates a JavaScript object that represents the component. This object looks like this:

{
  type: MyButton,
  props: {}
}

This object has two important properties:

  1. type: This refers to the component itself (MyButton).
  2. props: This refers to the properties (or "props") passed to the component. In this case, there are no props passed, so it's an empty object.

How React Uses Components

React uses both function and class components in a similar way. Whether you define your component as a function or a class, React calls the component to get the description of the UI (the JSX), then builds a tree of virtual DOM nodes. This process is recursive — meaning if you have a component that contains other components, React will also create virtual DOM nodes for those components.

For example, let’s say you have a parent component and a child component:


function Parent() {
  return ;
}

Here, React will first create the virtual DOM node for the Parent component. Then, it will call the MyButton component inside the Parent component and create the virtual DOM node for it too. It does this recursively, meaning if components are nested, React will go through each one to build the complete tree of virtual DOM nodes.

4. Reconciliation and Virtual DOM

One of the main reasons React is known for its performance is its reconciliation algorithm and the use of the Virtual DOM. But what does this mean, and how does it help make React fast and efficient? Let's break it down step by step.

What is the Virtual DOM?

To understand how React updates the real DOM efficiently, we first need to understand the Virtual DOM. The Virtual DOM is a lightweight, in-memory representation of the real DOM. It’s essentially a copy of the UI, but it’s not the actual elements on the page. React uses this virtual representation to figure out what changes need to be made to the real DOM, without directly manipulating it right away. This saves a lot of time and resources because interacting with the real DOM can be slow, especially when there are many elements.

When State Changes
Now, let’s say something changes in your app, such as when a user clicks a button or enters text in an input field. This is a state change, and when state changes, React follows these steps:

  1. Rebuild the Virtual DOM: React re-renders the component and rebuilds the entire Virtual DOM tree. This means React creates a new in-memory copy of the UI based on the updated state.
  2. Compare the Virtual DOMs: React then compares the newly created Virtual DOM tree to the previous one. This comparison helps React figure out what has changed between the two.
  3. Apply Necessary Updates: After React has identified the differences, it applies only the necessary updates to the real DOM. React doesn’t update the entire real DOM — just the parts that have changed. This process is called reconciliation.

Virtual Dom Comparision

How Reconciliation Works
React's reconciliation process follows specific rules to make updates efficient. Let’s go through them one by one:

  1. Elements of Different Types: If the type of the element has changed (for example, if a is replaced by a ), React will completely replace the existing DOM node with a new one. This ensures that React doesn’t try to keep any unnecessary information when the structure changes.

    For example, if you change a

    to a // Updated

    React will replace the entire

    node with a new
    ); }

    Internally, React builds a hook state array something like:

    hooks = [0, 'Guest']; // index 0 = count, index 1 = name
    
    

    When you click the button, setCount updates index 0 and triggers a re-render. During the re-render, React reads from the hooks array in the same order to re-apply the useState calls.

    6. React Fiber: The Core Architecture

    React Fiber

    What is Fiber?

    React Fiber is the engine that powers React behind the scenes. Introduced in React 16, Fiber was a complete rewrite of React's internal rendering logic to improve performance, responsiveness, and flexibility.

    Before Fiber, React used a synchronous, blocking update model — which meant large updates could freeze the UI. Fiber changed all that by making rendering incremental and interruptible.

    Think of Fiber like a highly efficient project manager. Instead of doing all tasks at once (which might delay user interactions), it breaks them into smaller chunks and pauses/reorders them based on priority.

    How Does Fiber Work?

    Fiber breaks down rendering work into units, and schedules them based on priority. This makes React feel faster and more responsive — especially on slow devices or large apps.

    Here’s how Fiber improves the update process:

    Incremental rendering: React doesn’t process the whole component tree in one go. It can pause work and resume it later.

    Interruptible rendering: If a more urgent update (like a user typing) comes in, React can pause current work and handle the urgent update first.

    Smart scheduling: React uses a scheduler that decides what updates to do now, and what can wait (e.g., transitions or animations).

    The Fiber Node

    Each React element or component becomes a Fiber node — a JavaScript object with links to related nodes and properties. Imagine it like a tree where each node is a task with connections to siblings, parents, and children.

    Here’s what a simplified Fiber node looks like:

    {
      type: MyComponent,        // Function or DOM type
      child: [childNode],       // First child in the tree
      sibling: [nextSibling],   // Next sibling on the same level
      return: [parentNode],     // Parent fiber
      props: { ... },           // Props passed to the component
      stateNode: DOM or class   // Reference to DOM node or class instance
      alternate: [oldFiber]     // Used for diffing with previous version
    }
    This forms a linked list/tree structure, allowing React to efficiently walk through and update parts of the UI.
    
    

    Scheduling and Priorities

    React assigns each update a priority level, called a lane. For example:

    • urgent: typing input, clicks
    • default: UI updates
    • transition: animations or routes
    • idle: low-priority background tasks

    React uses a loop (like requestIdleCallback) to decide which updates to work on, and can yield (pause) between chunks so it doesn’t block the browser.

    Think of it like juggling — React keeps many tasks in the air, but it knows which ones to catch first based on importance.

    The Commit Phase

    Once React finishes building the updated Fiber tree (this is called the render phase), it enters the commit phase, where:

    1. DOM updates are applied (in a batch).
    2. Effects like useLayoutEffect and useEffect are executed.

    By separating the render and commit phases, React ensures smooth visual updates and allows powerful features like concurrent rendering, Suspense, and transitions.

    React Fiber is the heart of modern React, enabling:

    • Smooth, responsive interfaces
    • The ability to pause and resume rendering
    • Smarter prioritization of UI tasks

    This internal architecture makes it possible to build complex apps without worrying about performance — React takes care of the heavy lifting behind the scenes.

    Conclusion

    Diving into React's internals isn’t just for advanced developers — it’s a powerful way to level up your day-to-day React skills. When you understand how React works under the hood — from JSX and component trees to the Fiber architecture and hooks — everything starts to make more sense.

    You begin to write cleaner code, avoid common pitfalls, and debug with confidence because you know why React behaves the way it does. You realize that things like stable hook order, using keys in lists, or why rendering is fast and batched aren’t just “rules” — they’re design decisions based on a deeply thought-out engine.

    React is not just a library — it’s a state machine, a scheduler, and a rendering engine all working in sync to deliver fluid user experiences. And the best part? It gives you a simple, declarative API to work with, so you can focus on your product while it handles the complexity.

    So, the next time something in your app feels "off," or you want to improve performance, remember: React’s inner workings are not a black box.

    Keep Learning , Keep Growing