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
anduseEffect
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(
, 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.
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:
- type: This refers to the component itself (MyButton).
- 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:
- 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.
- 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.
- 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.
How Reconciliation Works
React's reconciliation process follows specific rules to make updates efficient. Let’s go through them one by one:
-
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