How React Actually Works Under the Hood: From useState to Re-rendering

If you've been learning React, you've probably written code like this hundreds of times:
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
function increment() {
setCount(count + 1);
}
return (
<>
<h1>{count}</h1>
<button onClick={increment}>Increment</button>
</>
);
}
It looks simple.
Click the button.
The number increases.
But what actually happens behind the scenes?
Where does React store count?
Does setCount() immediately change the value?
Why does the component run again?
How does the browser know what to update?
These are the questions that separate someone who can use React from someone who truly understands React.
In this article, we'll follow the entire lifecycle of a React component—from the moment your browser requests the page until React updates a single piece of text on your screen.
Before React Exists
Let's start at the very beginning.
Imagine you've created a React application using Vite.
Your HTML file probably looks something like this:
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
Notice something interesting?
The page contains almost nothing.
There is no navbar.
No hero section.
No buttons.
No cards.
Just an empty <div>.
That empty element is where your entire React application will eventually appear.
The Browser Loads Your Application
When you open the page, the browser downloads the JavaScript referenced by:
<script type="module" src="/src/main.tsx"></script>
This executes your application's entry point.
A typical main.tsx looks like this:
import ReactDOM from "react-dom/client";
import App from "./App";
ReactDOM.createRoot(document.getElementById("root")!).render(
<App />
);
This line is where everything begins.
ReactDOM.createRoot(...).render(<App />);
React is essentially being told:
"Start rendering this application inside the
#rootelement."
React Calls Your Component
React now executes your component like a normal JavaScript function.
function App() {
return <h1>Hello React</h1>;
}
Notice something important.
React doesn't read your JSX directly.
Instead, it calls your function.
React
↓
Calls App()
↓
Receives JSX
Your component is simply a JavaScript function that returns JSX.
JSX Isn't HTML
Many beginners think JSX is HTML.
It isn't.
This:
<h1>Hello React</h1>
is transformed during compilation into something similar to:
React.createElement(
"h1",
null,
"Hello React"
);
or, with the modern JSX transform:
jsx("h1", {
children: "Hello React",
});
JSX is simply a convenient syntax for describing user interfaces.
React Creates React Elements
The JSX becomes plain JavaScript objects called React Elements.
For example:
<h1>Hello</h1>
roughly becomes:
{
type: "h1",
props: {
children: "Hello"
}
}
React Elements are descriptions of what the UI should look like.
They are not actual DOM nodes.
React Builds the Virtual DOM
Using those React Elements, React constructs an internal tree called the Virtual DOM.
Think of it as React's blueprint.
App
↓
div
├── h1
├── p
└── button
This exists only in JavaScript memory.
Nothing has appeared on the screen yet.
React Creates Real DOM Nodes
Once React knows what the UI should look like, ReactDOM creates actual browser DOM nodes.
Virtual DOM
↓
Real DOM
↓
Browser
Only now does your page begin to appear.
The browser then performs its normal rendering pipeline:
DOM
↓
CSSOM
↓
Render Tree
↓
Layout
↓
Paint
↓
Composite
React does not replace the browser's rendering engine.
It simply tells the browser what DOM to create or update.
Where Does useState Store State?
Consider this component.
const [count, setCount] = useState(0);
Many developers assume the value is stored inside the component function.
It isn't.
The component function is recreated every time it runs.
Instead, React stores state internally.
A simplified mental model looks like this:
React Memory
Component
State #1
count = 0
State #2
...
State #3
...
When your component executes again, React remembers:
"The first
useState()belongs tocount."
Instead of creating new state every render, React retrieves the existing value.
That's why changing:
useState(0)
to
useState(100)
doesn't suddenly reset the counter after the first render.
The initial value is only used the first time the component mounts.
What Actually Happens When You Click the Button?
Suppose you click:
<button onClick={increment}>
The browser detects the click event.
React's event system receives that event.
React calls:
increment();
Inside that function:
setCount(count + 1);
Contrary to popular belief, this does not immediately change count.
Instead, React schedules a state update.
Click
↓
increment()
↓
setCount()
↓
Update queued
React Schedules a Re-render
After scheduling the update, React decides:
"This component needs to render again."
React calls the component function again.
React
↓
Calls Counter()
Again
This surprises many beginners.
The entire function executes again.
Not just the button.
Not just the <h1>.
Everything inside the component runs again.
Does React Create New State Again?
No.
When React reaches:
const [count, setCount] = useState(0);
it doesn't create new state.
Instead, it looks inside its internal state storage.
Previously:
count = 0
After clicking:
count = 1
React returns:
count = 1
even though your code still says:
useState(0)
The 0 has already served its purpose.
React Creates a New Virtual DOM
Now React executes the component again.
It produces a brand-new Virtual DOM.
Old Virtual DOM
↓
New Virtual DOM
React now has two UI descriptions.
One before the update.
One after the update.
React Diffs Both Trees
React compares them.
This process is called reconciliation.
Suppose the old tree was:
<h1>0</h1>
The new tree is:
<h1>1</h1>
React notices:
Everything is identical
Except
The text changed
React Updates the Real DOM
Instead of rebuilding the entire page, React performs the smallest possible update.
Old
0
↓
New
1
Only the text node changes.
The button isn't recreated.
The <div> isn't recreated.
The page isn't refreshed.
React updates only what changed.
Why React Feels Fast
Many people believe React is fast because of the Virtual DOM.
That's only part of the story.
React is fast because it minimizes unnecessary work.
Instead of replacing the entire DOM:
Entire Page
↓
Rebuild Everything
React does this:
Find Difference
↓
Update One Node
Reducing DOM operations is important because DOM manipulation is generally much more expensive than working with JavaScript objects in memory.
A Common Misconception
Many beginners think:
"React searches for the button, runs the event handler, changes the HTML."
That's not quite what happens.
The real sequence is:
Browser detects click
↓
React receives event
↓
Event handler runs
↓
State update scheduled
↓
Component function runs again
↓
New Virtual DOM created
↓
Virtual DOM compared
↓
Real DOM updated
↓
Browser repaints
This explains why React components are often described as declarative.
You don't manually update the DOM.
You describe what the UI should look like for a given state, and React figures out the minimum set of DOM changes needed to make that happen.
Does React Re-render the Entire Page?
Technically, React re-executes the entire component function.
However, that does not mean it recreates the entire browser DOM.
Re-running JavaScript is relatively cheap.
Manipulating the DOM is comparatively expensive.
React uses reconciliation to ensure that only the necessary DOM updates occur.
This distinction is one of the most important concepts to understand when learning React.
The Complete React Rendering Flow
Here's the entire lifecycle in one diagram.
Browser requests page
↓
HTML loads
↓
Browser downloads JavaScript
↓
main.tsx executes
↓
React renders <App />
↓
Component function runs
↓
JSX becomes React Elements
↓
Virtual DOM created
↓
Real DOM created
↓
Browser renders page
↓
User clicks button
↓
Event handler runs
↓
setState schedules update
↓
Component function runs again
↓
New Virtual DOM created
↓
React compares old vs new
↓
Real DOM updated
↓
Browser repaints
Final Thoughts
React isn't "magical."
Under the hood, it's following a predictable process:
Your components are just JavaScript functions.
JSX is converted into JavaScript objects called React Elements.
React builds a Virtual DOM from those elements.
State is stored internally by React—not inside your component function.
Calling
setStateschedules a re-render rather than changing state immediately.React compares the old and new Virtual DOM trees.
Only the necessary changes are applied to the browser's DOM.
Once you understand this flow, concepts like useState, re-rendering, reconciliation, hooks, memoization, and even React Server Components become much easier to reason about.
The goal isn't just to know how to write React components—it's to understand what React is doing on your behalf every time your application renders. That understanding will make you a more confident developer, help you debug issues more effectively, and allow you to build faster, more maintainable applications.