11 August, 2022 | 7 min read

How React Works: What is JSX?

When I started learning React, several questions tripped me up.

  • Why are we using className instead of class if we’re writing HTML?
  • What is React’s syntax for rendering something for each item in a list? What about conditional rendering?
  • Why can’t I use if statements inside the JSX?

In addition, I had no idea how React took my code and turned it into a working app. I didn’t know what I could push or change. The three tools in my belt were trial, error, and luck.

I want to lift the hood of React and show what’s happening behind the scenes. Although you don’t need to know this to use React, understanding it gives us more confidence and helps us explain some things that initially appear strange.

Rendering With JavaScript

Let’s start at the beginning: the hello world app that everyone starts with when they are learning HTML:

<body>
  <div>Hello World</div>
</body>

This works great until we want to start rendering anything dynamic. For example, what if we wanted to show the current date?

We can render our “Hello World” div using JavaScript:

<body>
  <div id="root"></div>

  <script type="module">
    const element = document.createElement('div');
    element.textContent = 'Hello World';

    const rootElement = document.getElementById('root');
    rootElement.append(element);
  </script>
</body>

Notice how there are two steps to rendering the div:

  1. Create the element in memory
  2. Attach it to the DOM

Until we append element to rootElement, it’s just sitting in memory. Let’s take a look at how JavaScript stores element.

Logging the element to the console shows us something like this:

div
  • accessKey: ""
  • align: ""
  • ariaAtomic: null
  • ariaAutoComplete: null
  • className: ""
  • textContent: ""

It’s an object with a huge list of properties (which should look familiar 👀).

If we want to update the element to give it a property (e.g. className), we can mutate the object:

<body>
  <div id="root"></div>

  <script type="module">
    const element = document.createElement('div');
    element.textContent = 'Hello World';
    element.className = 'container';

    const rootElement = document.getElementById('root');
    rootElement.append(element);
  </script>
</body>

Rendering With React

This works great for trivial applications but it’s going to quickly grow out of control once we start adding more DOM elements, managing state, etc.

This is where React steps in to save the day.

React follows a similar, two-step process to rendering elements:

  1. Create the element in memory using React.
  2. Render the React elements to the DOM using ReactDOM.
const element = React.createElement('div', {
  className: 'container',
}, 'Hello World');

const rootElement = document.getElementById('root');
ReactDOM.createRoot(rootElement).render(element);

As we did before, let’s console.log our element and see how React is storing it in memory:

element
{
  $$typeof: Symbol(react.element),
  key: null,
  props: {className: 'container', children: 'Hello World'},
  ref: null,
  type: "div",
  _owner: null,
  _store: {validated: false},
  _self: null,
  _source: null,
}

It’s once again just an object with properties. There’s no magic involved, just JavaScript.

JSX Magic

It’s possible that some of you will have used React but never seen React.createElement. You might be wondering what this has got to do with how we write idiomatic React components.

Instead of a string being used as our element’s child, we could pass another React element (or even a list of elements).

Each of those elements could also have child elements.

This can quickly get confusing with our React.createElement.

JSX was created to let us write our JavaScript code in a way that looks like HTML. But it’s important to realise that JSX is not HTML. It’s “syntactic sugar for the React.createElement(component, props, ...children) function (see the React docs).

What if I told you JSX is just syntactic sugar for JavaScript?

In other words, when we write:

<div className="container">Hello World</div>

It is compiled into:

React.createElement(
  'div',
  { className: 'container' },
  'Hello World'
);

You can test it out yourself and see what different JSX expressions compile to using the online Babel compiler.

Because JSX is just JavaScript, we can even log the JSX to see what we get:

const element = <div className="container">Hello World</div>
console.log(element)
element
{
  $$typeof: Symbol(react.element),
  key: null,
  props: {className: 'container', children: 'Hello World'},
  ref: null,
  type: "div",
  _owner: null,
  _store: {validated: false},
  _self: null,
  _source: null,
}

It’s the same as we had before. 🤯

We can use curly brackets to add JavaScript expressions to our JSX:

const className = 'container';
return <div className={className}>Hello world</div>

That’s all we have with React - JavaScript and JSX (which gets compiled to JavaScript).

It’s JavaScript All The Way Down

Once we realise that writing JSX is the same as calling React.createElement, it explains a lot of the quirkiness of React.

Why Does React Use ClassNames Instead Of Class?

In HTML land, we give an element a class using the class attribute:

<div class="container">Hello World</div>

But with React, we’re not in HTML land, we’re in JavaScript land. As we saw at the start, an element in JavaScript doesn’t use a class property, it uses a className property. So to give an element a class with React, we need to use className:

React.createElement(
  'div',
  { className: 'container' },
  'Hello World'
);

// or

<div className="container>Hello World</div>

Why Can’t I Use if Statements in JSX?

It can be tempting to wonder why we can’t do something like this in React:

<div className={if (myCondition) {
  return 'container';
}}>Hello World</div>

Because we’re in JavaScript land, whatever we pass as the value of a key in an object must be an expression. It doesn’t make any sense to pass an if statement:

React.createElement(
  'div',
  { className: if (myCondition) {
    return 'container';
  }},
  'Hello World'
);

Because we need to pass an expression, we can either pull the if statement out and create a variable, or we can use a ternary expression:

let myClassName = '';
if (myCondition) {
  myClassName = 'container';
}

return <div className={myClassName}>Hello World</div>

// or

return <div className={myCondition ? 'container' : ''}>Hello World</div>

For more information on the difference between expressions and syntax, I recommend reading Josh W. Comeau’s post on Statements Vs. Expressions.

What’s the React-Specific Syntax for X?

Most UI frameworks have their own unique syntax that you need to learn to use the framework.

For example, in Svelte you can conditionally render something by using #if:

{#if answer === 42}
  <p>what was the question?</p>
{/if}

You can also loop over a list of items using #each

<ul>
  {#each items as item}
    <li>{item.name}</li>
  {/each}
</ul>

It’s easy when learning React to ask what the React-specific syntax is for similar things. However, we can lean into two things we learned above to show that React doesn’t need special syntax:

  • JSX is compiled to JavaScript
  • We can escape JSX and write JavaScript expressions by using { ... }

So how do we conditionally render in React? In the same way that we conditionally call a function - an if statement (or in this case, since we can’t use statements, a ternary expression):

{answer === 42 ? <p>what was the question?</p> : null}

How do we loop over a list of items and render an element for each? In the same way that we loop over a list of items and return a new list - with the map function:

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

Seeing Through The Matrix

Although it’s not necessary to know how React works, I find that it helps me feel more confident using React. I don’t resort to trial and error to find the magic combination to make things work (or, at least, not as often).

React is JavaScript.

One of the most appealing aspects of React is that it’s just JavaScript. Learning React is learning JavaScript.

As time moves on, UI frameworks come and go. But as long as JavaScript is still used, React will make us better developers.


Follow me on Twitter or Bluesky to get updates on new posts.