Skip to content

Latest commit

 

History

History

README.md

Module 1: React Elements with createElement()

Before JSX, before components, before hooks — there's createElement. This is how React actually works under the hood: you describe your UI as a tree of plain JavaScript objects, and React takes care of putting them on screen efficiently.

You won't write createElement by hand in real projects (Module 2 introduces JSX, which is far more pleasant). But starting here gives you a solid mental model of what React is actually doing. When something confusing happens later — a render you don't expect, a key warning, a performance issue — understanding this layer will help you reason about it.

This module uses React from a CDN with plain HTML files. No build tools, no terminal commands. Just a script tag and a text editor.

What Is React, Really?

At its core, React is a library for building user interfaces out of components. It was created at Facebook (now Meta) in 2013, and it introduced an idea that's now everywhere in frontend development: declarative UI.

In traditional JavaScript, you tell the browser how to update things step by step — find this element, change its text, add this class. That's imperative programming. React flips it: you describe what the UI should look like for a given state, and React figures out how to make the DOM match. That's declarative programming.

This difference matters more as applications grow. Imperative DOM manipulation gets tangled and fragile. Declarative descriptions stay readable.

React.createElement()

Everything in React starts with createElement. It takes three arguments:

React.createElement(type, props, ...children)
  • type — the kind of element: an HTML tag name like 'h1', 'div', 'p'
  • props — an object of attributes: { className: 'title', id: 'main' } (or null if none)
  • children — what goes inside: text, numbers, other elements, or arrays of elements

Here's the simplest possible example:

const heading = React.createElement('h1', null, 'Hello, Academy!')

This creates a React element — a plain JavaScript object that describes an <h1> tag containing the text "Hello, Academy!" It doesn't actually create a DOM node yet. It's just a description.

Elements Are Just Objects

When you call createElement, you get back a plain object:

const element = React.createElement('h1', { className: 'title' }, 'Hello')

// element is roughly:
// {
//   type: 'h1',
//   props: {
//     className: 'title',
//     children: 'Hello'
//   }
// }

These objects are cheap to create. React can build thousands of them without touching the DOM. This is the foundation of the Virtual DOM — a lightweight tree of these objects that React keeps in memory.

Nesting Elements

Real UIs are trees — elements inside elements. You pass child elements as additional arguments:

const card = React.createElement(
  'div',
  { className: 'student-card' },
  React.createElement('h2', null, 'Toasty McPigeonfingers'),
  React.createElement('p', null, 'House: Scarybird'),
  React.createElement('p', null, 'Level: 45')
)

This describes:

<div class="student-card">
  <h2>Toasty McPigeonfingers</h2>
  <p>House: Scarybird</p>
  <p>Level: 45</p>
</div>

It's verbose — and that's fine for now. You'll appreciate JSX much more after writing createElement by hand for a module.

Using JavaScript Data

Since elements are built with JavaScript, you can use variables, template strings, and expressions freely:

const student = {
  name: 'Luna Moonwhisper',
  house: 'Huftybadger',
  magicLevel: 38,
  health: 100
}

const profile = React.createElement(
  'div',
  { className: 'student-card' },
  React.createElement('h2', null, student.name),
  React.createElement('p', null, `House: ${student.house}`),
  React.createElement('p', null, `Magic Level: ${student.magicLevel}`),
  React.createElement('p', null, `Health: ${student.health}`)
)

This is a key insight: React elements are generated by JavaScript. Your data drives the UI.

Rendering to the Page

Creating elements is only half the story. To actually show them in the browser, you need ReactDOM:

const container = document.getElementById('root')
const root = ReactDOM.createRoot(container)
root.render(profile)

createRoot tells React which DOM element to manage. render takes your element tree and builds the actual DOM nodes inside that container.

Your HTML file just needs a <div id="root"></div> and script tags for React and ReactDOM:

<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

The Virtual DOM

When you call render, React doesn't just dump HTML into the page naively. It does something clever:

  1. It builds a tree of element objects (the Virtual DOM)
  2. It compares that tree to what's currently on screen
  3. It calculates the minimal set of DOM operations needed
  4. It applies only those changes

This diffing process means React can update the UI efficiently even when your data changes frequently. You describe the full desired state, and React figures out the cheapest way to get there.

For now, with static data, this doesn't matter much. But once you add state and interactivity (Modules 3+), the Virtual DOM becomes essential for performance.

Rendering Lists

Applications rarely display a single item. To render a collection, use .map() to transform an array of data into an array of elements:

const students = [
  { name: 'Toasty McPigeonfingers', house: 'Scarybird', magicLevel: 45 },
  { name: 'Thor Ironforge', house: 'Liondudes', magicLevel: 62 },
  { name: 'Luna Starlight', house: 'Huftybadger', magicLevel: 38 }
]

const studentElements = students.map(student =>
  React.createElement(
    'div',
    { className: 'student-card' },
    React.createElement('h3', null, student.name),
    React.createElement('p', null, `House: ${student.house}`),
    React.createElement('p', null, `Level: ${student.magicLevel}`)
  )
)

root.render(
  React.createElement('div', null, ...studentElements)
)

Each item in the array becomes an element. React renders the array as siblings inside the parent div. This pattern — data array transformed into element array — is one of the most fundamental patterns in React.

Common Mistakes

Forgetting to call render. Creating elements does nothing by itself. You must pass them to root.render() to see anything on screen.

Passing props wrong. The second argument must be an object or null — not a string. createElement('h1', 'Hello') won't work. It should be createElement('h1', null, 'Hello').

Using class instead of className. React uses className for CSS classes because class is a reserved word in JavaScript.

Mutating objects. React elements are meant to be immutable. Don't try to change an element's props after creating it — create a new one instead.

Exercises

Two quests to practise these fundamentals:

Quest 1: Wizard Identity — Create a student object with your wizard's stats (name, house, magic level, health) and render it to the page using createElement.

Start Quest 1 →

Quest 2: Student Registry — Build an array of five students and render them all as a list using .map() and createElement.

Start Quest 2 →

Running the Code

No build tools needed for this module. Open the HTML files directly in your browser:

demo/index.html    — the working example

Open your browser's developer console to see any output or errors.

The slides introduce React, explain the Virtual DOM, and show createElement with diagrams:

cd slides
npm install
npm run dev

Module 2: JSX and Components →