Skip to content
Twilight React Playground
ثيم رائدaren

From document.createElement to JSX

Beginner8 min

The same product card built with DOM calls and with JSX, side by side, and the few rules JSX adds.

In plain JavaScript you build a page piece by piece: document.createElement('h3') makes a heading, textContent puts words in it, append hangs it inside another element. When the data changes, you find the elements again and fix them, or throw them away and build new ones.

JSX lets you write the same tree the way it looks in HTML, directly inside JavaScript. You describe what the card should be for the current data, and React works out what to change on the page.

The same card, built twice

Both cards below read the same three controls. The left one is drawn with DOM calls, the right one with JSX. Type in the name, then tick Sold out, and watch how many elements each approach creates for one change.

document.createElement
Elements created by the last change:
JSX

Leather armchair

228 SAR

Elements created by the last change: 0

The DOM version builds a whole new card for every keystroke. React changes only the text that differs, and creates one element when the badge appears. That matters more than speed: an element that is rebuilt loses its focus, its text selection and its scroll position.

card.js (plain DOM)
function renderCard(container, product) {
  const card = document.createElement('article');

  const title = document.createElement('h3');
  title.textContent = product.name;

  const price = document.createElement('p');
  price.textContent = product.price + ' SAR';

  card.append(title, price);

  if (product.soldOut) {
    const badge = document.createElement('span');
    badge.textContent = 'Sold out';
    card.append(badge);
  }

  container.replaceChildren(card); // throw the old card away, put the new one in
}

// And you must remember to draw again after every change:
nameInput.addEventListener('input', () => {
  product.name = nameInput.value;
  renderCard(container, product);
});
ProductCard.jsx
export function ProductCard({ name, price, soldOut }) {
  return (
    <article>
      <h3>{name}</h3>
      <p>{price} SAR</p>
      {soldOut && <span>Sold out</span>}
    </article>
  );
}

What JSX really is

JSX is not HTML, and browsers cannot run it. The build turns every tag into a function call that returns a plain object describing the element. This is roughly what the card above becomes:

after the build
import { jsx as _jsx, jsxs as _jsxs } from 'react/jsx-runtime';

export function ProductCard({ name, price, soldOut }) {
  return _jsxs('article', {
    children: [
      _jsx('h3', { children: name }),
      _jsxs('p', { children: [price, ' SAR'] }),
      soldOut && _jsx('span', { children: 'Sold out' }),
    ],
  });
}

So {name} is just a JavaScript value placed in the children of the h3, and {soldOut && …} is an ordinary && expression: when soldOut is false, React draws nothing there.

The rules you meet on day one

  • Curly braces hold any expression: {price * 2}, {items.length}, {isRTL ? 'rtl' : 'ltr'}. Statements such as if and for do not fit inside; use &&, ? : or .map().
  • Attributes use JavaScript names: className instead of class, htmlFor instead of for, onClick instead of onclick.
  • `style` takes an object: style={{ marginInlineStart: 8, fontWeight: 700 }}, with camelCase property names; a plain number on a size such as marginInlineStart means pixels.
  • Every tag is closed: <img />, <br />, <input />.
  • One outer element: return one element, or wrap siblings in a fragment, <>…</>.
  • Lists need a `key`: products.map((p) => <li key={p.id}>{p.name}</li>), so React can tell the items apart when the list changes.
In engine terms
  • A theme's tsconfig.json sets "jsx": "react-jsx", the automatic runtime shown above: no import React is needed in .tsx files.
  • React 19 compares the new description with the previous one after each render and applies only the difference to the DOM. That comparison is what the right-hand counter observes, through a MutationObserver.
  • Engine components are written the same way: ProductCard (src/components/product/ProductCard.tsx) is a function returning JSX with conditional badges and prices.
Check yourself

How do you show product.name inside a heading in JSX?