Back to Blog
ReactJS

How React Renders HTML: A Deep Dive into JSX, the Virtual DOM, and Performance

10/9/2025
5 min read
How React Renders HTML: A Deep Dive into JSX, the Virtual DOM, and Performance

Confused about how React renders HTML? This in-depth guide explains JSX, the Virtual DOM, reconciliation, and best practices to master rendering in your React applications.

How React Renders HTML: A Deep Dive into JSX, the Virtual DOM, and Performance

How React Renders HTML: A Deep Dive into JSX, the Virtual DOM, and Performance

How React Renders HTML: A Deep Dive into JSX, the Virtual DOM, and Performance

If you're learning web development, you've undoubtedly heard of React. It's the library that powers the front-ends of giants like Facebook, Instagram, and Netflix. But have you ever stopped to wonder how it actually works? How do those elegant, functional components you write transform into the interactive buttons, forms, and layouts that users see and click on in their browsers?

The answer lies in one of React's core concepts: rendering.

At its heart, rendering is the process of turning your component's code into the HTML that paints the screen. But React doesn't just dump static HTML onto the page. It manages a dynamic, living document that can update efficiently in response to data changes. Understanding this process is the key to writing fast, reliable, and bug-free React applications.

In this comprehensive guide, we're going to pull back the curtain. We'll start with the fundamental question of what "rendering" means in React, journey through the magic of JSX, demystify the legendary Virtual DOM, and equip you with best practices to become a rendering master.

To learn professional software development courses such as Python Programming, Full Stack Development, and MERN Stack, visit and enroll today at codercrafter.in.

What Does "Rendering" Mean in React?

Let's get our definitions straight. In the context of traditional web development, rendering often meant the server generating a complete HTML file and sending it to the browser. The browser would then parse it and display it—a process known as server-side rendering (SSR).

React, however, primarily uses a different approach called Client-Side Rendering (CSR). Here’s the breakdown:

  1. Initial Load: The server sends a nearly empty HTML file and a JavaScript bundle (containing your React code) to the browser.

  2. The "Aha!" Moment: The browser downloads the JavaScript and executes it.

  3. Rendering Kicks In: React takes over. It runs your component code, which returns a description of what the UI should look like (using JSX, which we'll cover next).

  4. DOM Manipulation: React then takes this description and meticulously translates it into a set of instructions to create actual HTML elements. It updates the Browser's Document Object Model (DOM)—the programming interface for the web page.

This entire process of converting your components into DOM nodes is what we call rendering in React. And it doesn't just happen once. Every time your component's state or the props it receives change, React triggers a re-render to keep the UI in sync with the data.

JSX: The Blueprint for Your UI

You can't talk about React rendering without talking about JSX. If you've seen React code before, you've seen JSX. It looks like HTML was plopped right inside your JavaScript.

jsx

function Greeting() {
  return <h1 className="welcome">Hello, World!</h1>;
}

But here's the secret: JSX is not HTML. It's a syntax extension for JavaScript that produces React "elements".

What Happens Under the JSX Hood?

Your JSX code is compiled down to regular JavaScript before it ever gets to the browser. Tools like Babel handle this transformation. The above Greeting component gets compiled to this:

jsx

function Greeting() {
  return React.createElement('h1', { className: 'welcome' }, 'Hello, World!');
}

React.createElement() is a function that creates a plain JavaScript object. This object describes the element you want to see on the screen. The output for our <h1> would look something like this:

js

{
  type: 'h1',
  props: {
    className: 'welcome',
    children: 'Hello, World!'
  }
}

These objects are the React elements. They are lightweight descriptions of the desired UI. Think of them as very detailed blueprints. They are not the actual house (the DOM element); they're just the instructions for how to build it. This is a crucial distinction because working with these simple objects is incredibly fast compared to manipulating the real, heavy-weight DOM.

JSX Rules and Gotchas

Since JSX is closer to JavaScript, it has a few key differences from HTML:

  • className instead of class: Because class is a reserved keyword in JavaScript.

  • Self-Closing Tags: Tags like <img> and <input> must be self-closed with / -> <img />.

  • CamelCase Property Names: onclick becomes onClick, tabindex becomes tabIndex.

  • JavaScript Expressions in Curly Braces: You can embed any valid JavaScript expression inside {}.

jsx

function UserCard({ userName, avatarUrl }) {
  const displayName = userName || 'Anonymous';
  return (
    <div className="user-card">
      <img src={avatarUrl} alt="User Avatar" style={{ width: '50px', borderRadius: '50%' }} />
      <h2>{displayName}</h2>
      <button onClick={() => alert(`Hello, ${displayName}!`)}>Say Hello</button>
    </div>
  );
}

In this example, we're using variables, logical operators, and inline functions inside the {}. This power is what makes JSX so dynamic.

The Rendering Pipeline: From Component to Screen

Now that we understand JSX and React elements, let's connect the dots and see the entire rendering pipeline from start to finish.

Step 1: The Render Trigger

A render is triggered in two main scenarios:

  1. Initial Render: When the app first loads (ReactDOM.render(<App />, document.getElementById('root')) or the new root.render in React 18).

  2. Re-render: When a component's state (via useState or useReducer) changes, or when it receives new props from its parent.

Step 2: The Render Phase

When a render is triggered, React calls your component function. This function executes, and the JSX is compiled into a tree of React elements. This tree is a snapshot of what the UI should be at that specific moment in time, based on the current state and props.

It's vital to understand that rendering in this phase is pure and has no side effects. Given the same props and state, a component should always return the same JSX. This predictability is what makes React components easy to reason about.

Step 3: The Commit Phase & The Secret Sauce: Virtual DOM and Reconciliation

Here's where the magic happens. React now needs to take the new tree of React elements (the new blueprint) and update the actual browser DOM. Doing this naively—by rebuilding the entire DOM from scratch on every change—would be painfully slow.

This is where the Virtual DOM (VDOM) comes in.

The VDOM is not a mysterious, complex technology. It's simply a representation of the real DOM kept in memory. It’s a JavaScript object that describes the structure of your UI. When a render is triggered, React creates a new VDOM tree.

Now for the critical part: Reconciliation.

Reconciliation is React's diffing algorithm. It compares the new VDOM tree with the previous one ("diffs" them) to identify exactly what has changed.

jsx

// Initial Render
// VDOM Tree: <div><h1>Hello</h1><p>World</p></div>

// After State Change
// New VDOM Tree: <div><h1>Hello</h1><p>React!</p></div>

In this example, React's diffing algorithm would quickly see that only the text content of the <p> tag changed from "World" to "React!". It doesn't need to touch the <div> or the <h1>.

Step 4: The Final Step: DOM Update

Once React knows the minimal set of changes (the "diff"), it commits these changes to the real DOM. In our example, it would find the real <p> DOM node and update its textContent property. This process is batched and highly optimized for performance.

This cycle of render (creating the new VDOM) -> reconcile (diffing) -> commit (updating the real DOM) is the heart of React's rendering model.

Real-World Use Case: A Dynamic Task List

Let's see this entire process in action with a classic example: a task list.

jsx

import { useState } from 'react';

function TaskList() {
  const [tasks, setTasks] = useState([
    { id: 1, text: 'Learn React', isCompleted: true },
    { id: 2, text: 'Build a project', isCompleted: false }
  ]);
  const [inputValue, setInputValue] = useState('');

  const addTask = () => {
    if (inputValue.trim()) {
      setTasks([...tasks, { id: Date.now(), text: inputValue, isCompleted: false }]);
      setInputValue('');
    }
  };

  const toggleTask = (taskId) => {
    setTasks(tasks.map(task =>
      task.id === taskId ? { ...task, isCompleted: !task.isCompleted } : task
    ));
  };

  return (
    <div>
      <h1>My Task List</h1>
      <div>
        <input
          type="text"
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
          placeholder="Add a new task..."
        />
        <button onClick={addTask}>Add</button>
      </div>
      <ul>
        {tasks.map(task => (
          <li
            key={task.id}
            style={{ textDecoration: task.isCompleted ? 'line-through' : 'none' }}
            onClick={() => toggleTask(task.id)}
          >
            {task.text}
          </li>
        ))}
      </ul>
    </div>
  );
}

Rendering in Action:

  1. Initial Render: The component runs, tasks and inputValue are initialized, and React creates the initial VDOM with the list of two tasks.

  2. User Types: When you type in the input, onChange fires, updating the inputValue state. This triggers a re-render. The diffing algorithm sees that the input's value prop changed and the list is the same, so it only updates the real input element. The list is left untouched.

  3. User Adds a Task: Clicking "Add" calls addTask, which updates the tasks state with a new item. This triggers a re-render. The new VDOM tree now has a third <li>. React's diffing algorithm sees this new item and commits a single DOM insertion to add the new <li> to the real <ul>.

  4. User Completes a Task: Clicking a task calls toggleTask, which updates the isCompleted property for that specific task. The re-render creates a new VDOM. The diffing algorithm sees that only the style prop of one <li> has changed and updates only that specific property on that specific DOM node.

This is the power of React's rendering model. It provides a declarative way to build UIs ("I want the UI to look like this") while handling the complex, efficient, imperative DOM updates under the hood.

Best Practices for Optimal Rendering

A deep understanding of rendering naturally leads to knowing how to optimize it. Unnecessary re-renders can be a performance bottleneck.

1. The key Prop: Not Just a Suggestion, a Necessity

When rendering lists, the key prop is crucial. It helps React identify which items have changed, been added, or been removed. Using a stable, unique identifier (like an id from your data) allows the diffing algorithm to work optimally.

Bad: key={index}
Using the array index can lead to bugs and performance issues when the list order changes (e.g., adding an item to the top of the list).

Good: key={item.id}
A unique ID from your data is the best practice.

2. State Colocation: Keep State as Local as Possible

Lift state up only when necessary. If a piece of state is only used by one component, keep it in that component. This prevents unnecessary re-renders of parent and sibling components when that state changes.

3. Leverage React.memo, useMemo, and useCallback

These are performance optimization tools.

  • React.memo: A Higher-Order Component that memoizes your functional component. It will only re-render if its props change (using a shallow comparison).

  • useMemo: Memoizes the result of a costly calculation. const memoizedValue = useMemo(() => expensiveCalculation(a, b), [a, b]);

  • useCallback: Memoizes a function itself, preventing unnecessary re-renders of child components that depend on that function. const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]);

Use these sparingly! Don't optimize prematurely. Only reach for them if you've identified a real performance problem.

Frequently Asked Questions (FAQs)

Q: Does a component re-render when its props change?
A: Yes, that is a primary reason for a re-render.

Q: Does a parent re-rendering cause all children to re-render?
A: Yes, by default. When a parent component renders, it will, in turn, re-render all its direct child components. This is why state colocation and React.memo are important.

Q: Is the Virtual DOM the same as the Shadow DOM?
A: No, they are completely different concepts. The Shadow DOM is a browser technology for encapsulating CSS and markup in web components. The Virtual DOM is a programming concept implemented by libraries like React.

Q: How does React 18's Concurrent Rendering change this?
A: Concurrent Rendering is an advanced feature that allows React to work on multiple versions of the UI at the same time. It doesn't change the fundamental render/reconcile/commit process, but it makes it interruptible, allowing React to prioritize urgent updates (like user typing) over less urgent ones (like rendering a large list), leading to a more responsive user experience. Are you eager to master cutting-edge React features like these? Our MERN Stack course at codercrafter.in covers React 18 in depth, preparing you for modern front-end development.

Conclusion: Embracing the React Model

Rendering is the engine of your React application. By understanding the journey from JSX to the Virtual DOM and finally to the real DOM, you move from simply using React to truly thinking in React. You learn why components should be pure, why keys are non-negotiable in lists, and how to structure your state for optimal performance.

This knowledge empowers you to build applications that are not just functional, but also fast, scalable, and maintainable. It transforms you from a coder who writes components into a developer who architects robust user interfaces.

Remember, the goal isn't to eliminate all re-renders but to ensure that each render is necessary and as efficient as possible. Keep practicing, build projects, and don't be afraid to open the React DevTools Profiler to see rendering in action!

The journey to mastering React is incredibly rewarding. If you found this deep-dive helpful and are looking to build a rock-solid foundation in modern web development, consider taking the next step with us. To learn professional software development courses such as Python Programming, Full Stack Development, and the MERN Stack, visit and enroll today at codercrafter.in. Let's build the future, one component at a time.


Related Articles

Call UsWhatsApp