Skip to main content

Command Palette

Search for a command to run...

React Under the Hood: How React Actually Works

Updated
7 min read
React Under the Hood: How React Actually Works
P

Hi, I’m a software engineer with a strong focus on frontend development and open-source contributions. I enjoy solving real-world problems, improving developer workflows, and building systems that scale.

I believe in taking ownership of my work and paying close attention to quality and detail.

I'm here to document my learnings, ideas, and practical insights from building and shipping real products. If anything here resonates with you, feel free to reach out. I’m always happy to connect and learn from others.

When we first start learning React, we write components, use useState, see things magically update on the screen, and think “this is cool!“ But have you ever wondered what’s actually happening behind the scenes?

Understanding how React works internally will make us better developers. We will be able to write more efficient code, debug faster, and truly understand why React does things the way it does.

So, let’s dive deep into React’s internals in a simple and friendly way.


The Problem React Solves

Before React, updating the UI meant directly manipulating the DOM (Document Object Model). Let’s understand it with the code below:

// The old way - directly manipulating DOM
document.getElementById('counter').innerText = count;
document.getElementById('username').innerText = name;

Problems with this approach:

  • Slow: Every DOM manipulation causes the browser to recalculate styles, layout, and repaint

  • Error-prone: Easy to create bugs when manually tracking what needs updating

  • Hard to maintain: Complex UIs become a nightmare of DOM manipulation code

React's solution:

"Tell me what the UI should look like, and I'll figure out the most efficient way to update it."


What is the Virtual DOM?

Think of the Virtual DOM like a blueprint of your house, while the Real DOM is the actual house.

Real DOM

  • The actual HTML elements you see in the browser

  • Heavy and expensive to manipulate

  • Causes reflows and repaints

Virtual DOM

  • A lightweight JavaScript object that represents the Real DOM

  • Fast to create and manipulate

  • Lives in memory, not in the browser

Let’s see how it looks:

Your React Component:

function Welcome() {
  return (
    <div className="container">
      <h1>Hello, React!</h1>
      <p>Welcome to my app</p>
    </div>
  );
}

Virtual DOM Representation (simplified):

{
  type: 'div',
  props: {
    className: 'container',
    children: [
      {
        type: 'h1',
        props: {
          children: 'Hello, React!'
        }
      },
      {
        type: 'p',
        props: {
          children: 'Welcome to my app'
        }
      }
    ]
  }
}

It's just a plain JavaScript object! Super lightweight and fast to work with.


Initial Render: Painting the Screen for the First Time

Let's walk through what happens when your React app loads for the first time. Let’s go step by step.

Step 1: You write JSX

function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Counter: {count}</h1>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

Step 2: JSX gets transformed to JavaScript

Babel converts the JSX to React.createElement() calls:

// What the code actually becomes
function App() {
  const [count, setCount] = useState(0);

  return React.createElement(
    'div',
    null,
    React.createElement('h1', null, 'Counter: ', count),
    React.createElement(
      'button',
      { onClick: () => setCount(count + 1) },
      'Increment'
    )
  );
}

Step 3: React creates the Virtual DOM Tree

React executes the component function and builds a tree of objects like below:

{
  type: 'div',
  props: {
    children: [
      {
        type: 'h1',
        props: { children: ['Counter: ', 0] }
      },
      {
        type: 'button',
        props: {
          onClick: [Function],
          children: 'Increment'
        }
      }
    ]
  }
}

Step 4: React Renderer creates Real DOM

React takes this Virtual DOM and creates actual DOM elements:

// React internally does something like this:
const div = document.createElement('div');

const h1 = document.createElement('h1');
h1.textContent = 'Counter: 0';

const button = document.createElement('button');
button.textContent = 'Increment';
button.addEventListener('click', yourClickHandler);

div.appendChild(h1);
div.appendChild(button);
document.getElementById('root').appendChild(div);

Step 5: The browser paints the screen

The browser displays the Real DOM on your screen.

Visual Flow:


Re-rendering: When Things Change

Now the interesting part comes: what happens when we click that "Increment" button?

The Re-render process starts.

Step 1: State changes

// User clicks the button
<button onClick={() => setCount(count + 1)}>
  • setCount(1) is called

  • React marks this component as "needs update"

  • React schedules a re-render

Step 2: React calls the component again

function App() {
  const [count, setCount] = useState(0); // count is now 1

  return (
    <div>
      <h1>Counter: {count}</h1> {/* This will be "Counter: 1" */}
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

Step 3: React creates a NEW Virtual DOM

// New Virtual DOM
{
  type: 'div',
  props: {
    children: [
      {
        type: 'h1',
        props: { children: ['Counter: ', 1] } // Changed from 0 to 1
      },
      {
        type: 'button',
        props: {
          onClick: [Function],
          children: 'Increment'
        }
      }
    ]
  }
}

Step 4: The Magic - Reconciliation

React now has TWO Virtual DOM trees:

  • Old Virtual DOM (count: 0)

  • New Virtual DOM (count: 1)

React compares them and finds the difference.


Reconciliation: React's Diffing Algorithm

This is where React's genius shines. Instead of replacing the entire UI, React finds the smallest change needed.

The Diffing Process:

Let’s assume it's like a spot-the-difference game:

React's thought process:

  1. <div> - Same type, keep it

  2. <h1> - Same type, keep it, but...

  3. Text changed from "Counter: 0" to "Counter: 1" - Update needed!

  4. <button> - Same type, same content, keep it

React's update:

// Only this happens in the Real DOM: 
document.querySelector('h1').textContent = 'Counter: 1';

This is it! No need to recreate the entire component. Super efficient!

Reconciliation Rules:

React uses these rules when comparing:

Rule 1: Different Types → Replace Everything

// Old
<div>Hello</div>

// New
<span>Hello</span>

// React destroys the <div> and creates a new <span>

Rule 2: Same Type → Update Props

// Old
<div className="old">Hello</div>

// New
<div className="new">Hello</div>

// React keeps the <div>, just updates the className

Rule 3: Lists needed Keys

// Without keys - BAD
{items.map(item => <li>{item}</li>)}

// With keys - GOOD
{items. map(item => <li key={item.id}>{item}</li>)}

Why these keys matter:

// Initial list
<ul>
  <li key="1">Apple</li>
  <li key="2">Banana</li>
</ul>

// You add "Cherry" at the beginning
<ul>
  <li key="3">Cherry</li>  ← New item
  <li key="1">Apple</li>   ← React knows this is the same
  <li key="2">Banana</li>  ← React knows this is the same
</ul>

With keys: React knows to insert one new <li> at the top.

Without keys: React thinks all three items changed and updates all of them!


Fiber Architecture: React's Brain

In 2017, React introduced Fiber, a complete rewrite of the reconciliation engine in React 16.0.

Before React 16, rendering was synchronous. Long updates could block the main thread and freeze the UI.

What is Fiber?

Think of Fiber as React's task manager. It breaks rendering work into small chunks and can handle the following:

  • Pause work if something more important comes up

  • Resume work later

  • Prioritize urgent updates (like user input) over less urgent ones (like data fetching)

Difference with React Fiber:

Here are some priority levels for Fiber:

// React internally prioritizes updates: 

// HIGHEST PRIORITY (React updates immediately)
onClick, onInput, onKeyPress

// HIGH PRIORITY
Animations, transitions

// NORMAL PRIORITY
Data fetching, network responses

// LOW PRIORITY
Off-screen content, hidden components

// LOWEST PRIORITY
Analytics, logging

Example:

function App() {
  const [search, setSearch] = useState('');
  const [results, setResults] = useState([]);

  const handleSearch = (e) => {
    // HIGH PRIORITY - user input feels instant
    setSearch(e.target.value);

    // LOW PRIORITY - can be delayed
    startTransition(() => {
      const filtered = hugeDataset.filter(item => 
        item.includes(e.target.value)
      );
      setResults(filtered);
    });
  };

  return (
    <div>
      <input value={search} onChange={handleSearch} />
      {/* Input feels responsive even while filtering!  */}
      <Results data={results} />
    </div>
  );
}

Important note for clarity:

  • Fiber is an internal architecture, not a public API.

  • We don’t “use” Fiber directly, but features like useTransition, Suspense, and concurrent updates are built on top of it.


Conclusion

Let’s recap what we learned:

How React Works:

  1. JSX → JavaScript: Babel transforms your JSX to React.createElement() calls

  2. Virtual DOM: React creates lightweight JavaScript objects representing your UI

  3. Initial Render: React converts Virtual DOM to Real DOM and paints the screen

  4. State Changes: When state updates, React re-runs your component

  5. Reconciliation: React diffs old and new Virtual DOM to find minimal changes

  6. Efficient Updates: Only the changed parts of the Real DOM are updated

  7. Fiber: Breaks work into chunks for smooth, non-blocking updates

Key Takeaways:

  • Virtual DOM is a JavaScript representation of your UI

  • React only updates what actually changed

  • Fiber makes React responsive by prioritizing work

  • Keys help React identify elements in lists

Would you like to see this in action?

Open your browser DevTools, go to the Performance tab, and record your React app. You will see exactly when renders happen and how long they take!


Quick Resources

Happy coding!


If this article added value, feel free to share it with others.

For questions, feedback, or more insights, leave a comment or reach out to me on X or LinkedIn. I’d be happy to connect.

That’s it for today. Thanks for stopping by!