Good to knowReact Fundamentals · 01

Hello World in JS

A product card built with the raw DOM API: created in memory, then appended. What React automates, and why textContent is safe.

On this page · 8 sections
  1. In one minute
  2. The example: a product card by hand
  3. How it works
  4. textContent vs innerHTML
  5. Common mistakes
  6. Interview Q&A
  7. Related
  8. Sources

In one minute

Before React, a page changes through the DOM API: create a node, set its properties, append it somewhere. A node you create lives in memory until you append it to something that is already on the page. React is a much more convenient way to make the same calls, so it helps to see them once by hand.

You’ll be able to: explain what “on the page” means for a DOM node, why textContent is safe and innerHTML isn’t, and what work React takes off your hands.

card.ts
1const card = document.createElement('article')2const name = document.createElement('h2')3name.textContent = 'Ceramic Mug'4const price = document.createElement('p')5price.textContent = '$18.00'6card.append(name, price)78rootElement.append(card)
What you see
In memory: nothing yet
The page: an empty #root
document.body.contains(card): –
  1. const card = document.createElement('article')A real DOM node is created, but only in memory. Nothing on the page changes.
  2. const name = document.createElement('h2')A heading gets its text. Still in memory.
  3. const price = document.createElement('p')Same for the price.
  4. card.append(name, price)The pieces are put together into one card, still detached from the page.
  5. rootElement.append(card)Only now does the card appear: appending to a node that’s already on the page attaches it.

0 / 0
A product card built with nothing but the DOM API: create, change, then append. React will later do these steps for you.

The example: a product card by hand

The product store’s first lesson builds one card with nothing but DOM calls. Even the #root element is created by the code, to show it’s an ordinary node:

const rootElement = document.createElement('div')
rootElement.id = 'root'
document.body.append(rootElement)

const card = document.createElement('article')
card.className = 'product-card'

const name = document.createElement('h2')
name.textContent = product.name

const price = document.createElement('p')
price.className = 'price'
price.textContent = formatUSD(product.priceCents)

card.append(name, price)

At this point the card exists, but only in memory. One more line puts it on the page:

rootElement.append(card)

What the lesson logged before and after that line:

card built. On the page? false
after append. On the page? true
html: <article class="product-card"><h2>Ceramic Mug</h2><p class="price">$18.00</p></article>

How it works

textContent vs innerHTML

Product names come from users and APIs, so assume they can contain anything. The lesson renders a name with HTML in it both ways:

const safe = document.createElement('h2')
safe.textContent = evil // shown as text

const unsafe = document.createElement('h2')
unsafe.innerHTML = evil // parsed as HTML: the <img> is real
textContent → child elements: 0 · visible text: Mug <img src="x" onerror="window.__ran = true">
innerHTML → child elements: 1 · first child: IMG
onerror handler ran? true

textContent treats the string as text: no elements, the tags show up literally. innerHTML parses it, so the <img> became a real element and its onerror handler ran. That is how cross-site scripting (XSS) works.

React makes the safe choice the default: every string you render is treated as text. The only way to insert raw HTML is the deliberately alarming dangerouslySetInnerHTML prop.

Common mistakes

Interview Q&A

Why doesn't document.createElement alone put anything on the page?

It only builds a node in memory. A node becomes visible when it’s appended (or inserted) into a node that is already in the document. In the product store, the card logged On the page? false after it was fully built and true only after rootElement.append(card). This create → change → append sequence is the manual work React’s declarative API hides.

Why use textContent instead of innerHTML for text?

textContent never parses its input as HTML, so a value like Mug <img src="x" onerror="…"> stays visible text. innerHTML parses it into real elements, and the recording shows the <img>’s onerror handler actually ran. That’s an XSS hole. React escapes strings by default for the same reason, and makes raw HTML opt-in through dangerouslySetInnerHTML.

Is there anything special about the "root" DOM node React renders into?

No. It’s an ordinary element, created and appended like any other; the lesson even creates it in code. A library like React has no privileged access to the page: it makes the same DOM calls you could write by hand, just many of them, consistently.

Sources

Primary source: developer.mozilla.org/en-US/docs/Web/API/Document/createElement