codejump

AcademyReact

JSX and the Virtual DOM

JSX is sugar for function calls that build plain objects, not templating — and that object tree is the whole reason React can be fast.

3 min readUpdated

3 exercises

Waiting at the end of this page.

JSX is not a templating language

JSX looks like HTML embedded in JavaScript, and that resemblance is exactly what trips people up. It is not a string, not a template, and the browser never sees it — a build step (Babel, the TypeScript compiler, or the SWC transform Next.js uses) turns it into ordinary function calls before any of this reaches a runtime.

JSX
const heading = <h1 className="title">Hello, {name}</h1>;

desugars to:

JavaScript
const heading = React.createElement(
  'h1',
  { className: 'title' },
  'Hello, ',
  name,
);

Three arguments: what to render, its props, and its children. createElement does not touch the DOM. It returns a plain, serialisable object:

JavaScript
{
  type: 'h1',
  props: {
    className: 'title',
    children: ['Hello, ', name],
  },
}

That object is a React element. It describes a node the way a blueprint describes a house — nothing has been built yet.

Why React wants a description before it acts

Mutating the real DOM is comparatively expensive: every attribute write can trigger layout recalculation, and a browser element carries far more state than the handful of props you set. If a component re-rendered by touching the DOM directly on every state change, updating a list of 1,000 rows because one of them changed would mean 1,000 rows worth of DOM writes, whether or not the other 999 actually changed.

Elements let React sidestep that. Because a render produces a tree of plain objects — cheap to create, cheap to throw away, cheap to compare — React can build the new tree, diff it against the previous one entirely in memory, and only then touch the real DOM, and only where the two trees actually differ. The object tree is usually called the virtual DOM; the diffing pass over it is reconciliation.

JavaScript
// Before: type 'p', children 'Count: 0'
// After:  type 'p', children 'Count: 1'
// React writes ONE text node update, nothing else on the page moves

The object representation is the mechanism, not a feature in its own right. Nobody imports "the virtual DOM" — you import React, write JSX, and the object tree is what happens on the way to the screen.

Components return elements, not markup

A function component's job is to return one element (or null), built from whatever data it was given:

JSX
function Greeting({ name }) {
  return <p>Hello, {name}</p>;
}

// Called like any other function that happens to be invoked via JSX:
<Greeting name="Ada" />
// → React.createElement(Greeting, { name: 'Ada' })
// → an element whose type is the FUNCTION Greeting, not a string

This is the detail that makes composition work: an element's type can be a string ('div', 'p', 'h1' — a DOM tag) or a function (your own component). React walks the tree, and every time it meets a function-typed element it calls that function to get the elements that function returns, recursively, until only DOM tags are left. Only then does it know what to actually paint.

The {} escape hatch

Inside JSX, {} drops you back into plain JavaScript — any expression, not a special templating syntax:

JSX
<ul>
  {items.map((item) => <li key={item.id}>{item.label}</li>)}
</ul>

items.map(...) is regular Array.prototype.map, returning an array of elements. JSX children accept arrays, so this is not special-cased list syntax — it falls straight out of "children are just arguments to createElement," the same as any other value would.

JavaScript
// The children argument to createElement can be:
// a string, a number, another element, an array of elements, or null/false
// (null and false render nothing — the common way to hide something conditionally)

The one thing worth remembering

JSX compiles to function calls. Function calls return plain objects. Those objects are cheap to build and diff, and that cheapness is the entire justification for the extra layer — React never asks "did I already build this exact DOM node," it asks "does this object differ from the last one," which is a much simpler question to answer fast.

Now practice it

1 challenge · 2 quizzes

Reading this page is the cheap half. These are the exercises that make you use it.