Ever wonder what React does?
Tags: react, jsx, babel
Published: 2021-05-10 19:12:44

_Photo by Ferenc Almasi @unsplash_ 

_[Read this article on my blog](https://javascript-for-breakfast.com/ever-wonder-what-react-does)_

When we use React and JSX in our web applications, it's important to remember that we are "just" using an abstraction of the browser API methods.

In fact, JavaScript has a set of **imperative** methods that you can use to interact with the DOM, while React abstracts those methods to offer your a **declarative** approach.

💡 If you're not sure what "imperative" and "declarative" means, here's a brief explanation:

- **Imperative** is a concept that implies telling **HOW** to do something (technically speaking)
- **Declarative** implies telling **WHAT** to do

This is why it's called an abstraction because we don't need to know HOW it's gonna be done, we just want it done. For more details about those concepts, I recommend you check [this great article](https://tylermcginnis.com/imperative-vs-declarative-programming/).

I think it's important (and interesting) to understand how these abstractions work, what they do, and how they do it. This gives you more confidence as a developer and allows you to use them more efficiently.

So, let me take you on a quick journey from the good old times to the beautiful React components of nowadays 🚀

## 1. The imperative way

Let's see how you can interact with the browser DOM with pure JavaScript. Our goal here is to render a paragraph on the page.

```html
<!-- index.html -->
<body>
  <script type="text/javascript">
    // First, we need to create a div that will be the root element
    const rootNode = document.createElement("div")
    // Let's give it the id "root" and the class "container"
    rootNode.setAttribute("id", "root")
    rootNode.setAttribute("class", "container")
    // And finally add it to the DOM
    document.body.append(rootNode)

    // Sweet 👌 Now we need to create our paragraph
    const paragraph = document.createElement("p")
    paragraph.textContent = "Welcome, dear user !"
    // and add it to the root div
    rootNode.append(paragraph)
  </script>
</body>
```

So basically, we tell the browser to create a `div` with the id _root_ and the class _container_, and to insert it inside the `body` tag. Then we create and add a paragraph inside that `div`. Here's the output:

![the imperative way - output](https://cdn.hashnode.com/res/hashnode/image/upload/v1620124450273/MCUia3oSw.png)

## 2. React APIs

Now let's change this to use React. We actually only need 2 packages:

1. **React**: responsible for creating React elements
2. **ReactDOM**: responsible for rendering those elements to the DOM

> React supports multiple platforms. ReactDOM is used when writing web applications. For mobile apps, you would use the appropriate React Native package to render your elements.

> Note: normally, you would create your project with a tool like [create-react-app](https://github.com/facebook/create-react-app) or get React and ReactDOM scripts from a package registry. Here we use a CDN for demonstration purposes

```html
<!-- index.html -->

<body>
  <!-- The root div is placed directly in the HTML -->
  <!-- We could also create it like before, and append it to the body -->
  <div id="root"></div>

  <!-- We import React and ReactDOM -->
  <script src="https://unpkg.com/react@17.0.0/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@17.0.0/umd/react-dom.development.js"></script>

  <script type="module">
    const rootNode = document.getElementById("root")

    // Create the paragraph
    const element = React.createElement("p", null, "Welcome, dear user !")

    // Render the paragraph inside the root node
    ReactDOM.render(element, rootNode)
  </script>
</body>
```

With this in place, the generated HTML is exactly the same as before, with the extra imports for React and ReactDOM:

![React APIs - output](https://cdn.hashnode.com/res/hashnode/image/upload/v1620124488912/Wf2fJyYaj.png)

`React.createElement()` takes three arguments: **type**, **props** and **children**. This means that if we wanted our paragraph to have the `className` "welcome-text", we would pass it as a prop:

```javascript
React.createElement("p", { className: "welcome-text" }, "Welcome, dear user !")
```

We could also pass the children as prop, instead of passing it as the third argument:

```javascript
React.createElement("p", {
  className: "welcome-text",
  children: "Welcome, dear user !",
})
```

The **children** prop can take an array for multiple children, so we could also do:

```javascript
React.createElement("p", {
  className: "welcome-text",
  children: ["Welcome,", "dear user !"],
})
```

Or we can even add all the children after the second argument, as individual arguments:

```javascript
React.createElement(
  "p",
  { className: "welcome-text" },
  "Welcome, ",
  "dear user !"
)
```

If you're curious about the element returned by `React.createElement`, it's actually quite a simple object that looks like this:

```javascript
{
  type: "p",
  key: null,
  ref: null,
  props: { className: "welcome-text", children: ["Welcome, ", "dear user !"]},
  _owner: null,
  _store: {}
}
```

The renderer's job, in our case `ReactDOM.render`'s job, is simply to interpret that object and create the DOM nodes for the browser to print. This is why React has a different renderer for each supported platform: the output will vary depending on the platform.

So, this is all great, but you can start to see what a pain it would be the create more complex UI by using just those APIs. For example, let's say we need to make the following changes to our page:

- Place the paragraph inside a div
- Give the div an `id` "container"
- "dear user" should be in **bold**
- Place a button inside the div, with the text "Say Hi" that logs "Hi !" in the console when clicked

Here's how we would implement those changes:

```javascript
<!-- index.html -->
<body>
  <div id="root"></div>

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

  <script type="module">
    const rootNode = document.getElementById("root")

    // Container div
    const element = React.createElement("div", {
      id: "container",
      children: [
        // Paragraph
        React.createElement("p", {
          className: "welcome-text",
          children: [
            "Welcome, ",
            // "dear user" text wrapped inside a strong tag
            React.createElement("strong", null, "dear user"),
            " !",
          ],
        }),
        // "Say Hi" button
        React.createElement("button", {
          onClick: () => console.log("Hi !"),
          children: "Say Hi",
        }),
      ],
    })

    // Render the paragraph inside the root node
    ReactDOM.render(element, rootNode)
  </script>
</body>
```

HTML Output:

```html
<div id="root">
  <div id="container">
    <p class="welcome-text">Welcome, <strong>dear user</strong> !</p>
    <button>Say Hi</button>
  </div>
</div>
```

While it works perfectly, I think it is safe to say that nobody wants to build UIs like this. And this is where JSX comes in.

## 3. JSX to the rescue

JSX is a syntax extension to JavaScript, and it allows us to do things like this:

```javascript
const paragraph = <p className="welcome-text">Welcome, dear user !</p>
```

The browser won't understand this by itself, so we need a compiler like [Babel](https://babeljs.io/) which will turn this code into a `React.createElement` call:

```javascript
const paragraph = React.createElement(
  "p",
  {
    className: "welcome-text",
  },
  "Welcome, dear user !"
)
```

JSX's power, besides being able to nest elements in an HTML-like way, resides in what is called "interpolation". Everything you put inside `{` and `}` will be left alone and use to compute the values of props and children of `createElement` :

```javascript
const ui = (
  <div id="greetings">
    Hello {firstname} {lastname} !
  </div>
)
```

Compiled version:

```javascript
const ui = React.createElement(
  "div",
  {
    id: "greetings",
  },
  "Hello ",
  firstname,
  " ",
  lastname,
  " !"
)
```

With JSX in our toolbox, we can now rewrite the previous implementation in a much more clean and easy way. We will include Babel as a CDN and change our script type to `text/babel` so that our JSX expressions get compiled down to `React.createElement` calls:

```javascript
<!-- index.html -->
<body>
  <div id="root"></div>

  <script src="https://unpkg.com/react@17.0.0/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@17.0.0/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>

  <script type="text/babel">
    const rootNode = document.getElementById("root")

    // Container div
    const element = (
      <div id="container">
        <p className="welcome-text">
          Welcome, <strong>dear user</strong> !
        </p>
        <button onClick={() => console.log("Hi !")}>Say Hi</button>
      </div>
    )

    // Render the paragraph inside the root node
    ReactDOM.render(element, rootNode)
  </script>
</body>
```

Much better 😇 Back in the browser, we can see our UI with the generated DOM (including our untouched "text/babel" script):

![jsx output](https://cdn.hashnode.com/res/hashnode/image/upload/v1620124565798/vohMndTSw.png)

If we take a look in the `<head>` tag, we can see that Babel added a `script` for us with the compiled version of our JavaScript and JSX:

![jsx compiled output](https://cdn.hashnode.com/res/hashnode/image/upload/v1620124583036/efaOm5SKp.png)

Babel basically compiles down all our JSX code to nested `React.createElement` calls for us. How nice of him. Thanks to interpolation, we can also use variables for things that we want to use more than once in our JSX:

```javascript
const rootNode = document.getElementById("root")

const greetingButton = (
  <button onClick={() => console.log("Hi !")}>Say Hi</button>
)

// Container div
const element = (
  <div id="container">
    {greetingButton}
    <p className="welcome-text">
      Welcome, <strong>dear user</strong> !
    </p>
    {greetingButton}
  </div>
)

// Render the paragraph inside the root node
ReactDOM.render(element, rootNode)
```

![jsx interpolation](https://cdn.hashnode.com/res/hashnode/image/upload/v1620124601109/X-pi2_W9M.png)

Compiled version (thanks again, Babel !):

```javascript
var rootNode = document.getElementById("root")

var greetingButton = React.createElement(
  "button",
  {
    onClick: function onClick() {
      return console.log("Hi !")
    },
  },
  "Say Hi"
)

// Container div
var element = React.createElement(
  "div",
  { id: "container" },
  greetingButton,
  React.createElement(
    "p",
    { className: "welcome-text" },
    "Welcome, ",
    React.createElement("strong", null, "dear user"),
    " !"
  ),
  greetingButton
)

// Render the paragraph inside the root node
ReactDOM.render(element, rootNode)
```

Now we could use a **function** instead of a **variable** for our button. This way, we could pass as **props** the text for the button and the message to log in the console:

```javascript
const rootNode = document.getElementById("root")

const greetingButton = (props) => (
  <button onClick={() => console.log(props.message)}>{props.children}</button>
)

// Container div
const element = (
  <div id="container">
    {greetingButton({ message: "Hi !", children: "Say Hi" })}
    <p className="welcome-text">
      Welcome, <strong>dear user</strong> !
    </p>
    {greetingButton({ message: "Bye !", children: "Say Bye" })}
  </div>
)

// Render the paragraph inside the root node
ReactDOM.render(element, rootNode)
```

And if we look at the compiled version of our `greetingButton` function:

```javascript
var rootNode = document.getElementById("root")

var greetingButton = function greetingButton(props) {
  return React.createElement(
    "button",
    {
      onClick: function onClick() {
        return console.log(props.message)
      },
    },
    props.children
  )
}

// Container div
var element = React.createElement(
  "div",
  { id: "container" },
  greetingButton({ message: "Hi !", children: "Say Hi" }),
  React.createElement(
    "p",
    { className: "welcome-text" },
    "Welcome, ",
    React.createElement("strong", null, "dear user"),
    " !"
  ),
  greetingButton({ message: "Bye !", children: "Say Bye" })
)

// Render the paragraph inside the root node
ReactDOM.render(element, rootNode)
```

We see that it is now a function returning a `React.createElement`, and its value is used as a `children` argument of the `createElement` for the main element.

I think you see where this is going...

## 4. React Components

With our `greetingButton`, we are one step away from the traditional React Components. In fact, it would be nice to be able to use it like this:

```javascript
const element = (
  <div id="container">
    <greetingButton message="Hi !">Say Hi</greetingButton>
    <p className="welcome-text">
      Welcome, <strong>dear user</strong> !
    </p>
    <greetingButton message="Bye !">Say Bye</greetingButton>
  </div>
)
```

But here's what happens if we do so, back in the browser:

![wrong casing for react component](https://cdn.hashnode.com/res/hashnode/image/upload/v1620124644557/qc0CQjSxw.png)

The buttons aren't "buttons", we just see their texts (= children) in the page. Because `<greetingButton>` is in the DOM without being a valid HTML tag, the browser doesn't know what to do with it. `ReactDOM` is telling us why in the console:

`Warning: <greetingButton /> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.`

`Warning: The tag <greetingButton> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.`

Because `greetingButton` doesn't start with an uppercase letter, Babel compiles our code to this:

```javascript
React.createElement("greetingButton", { message: "Hi !" }, "Say Hi"),
// ...
React.createElement("greetingButton", { message: "Bye !" }, "Say Bye")
```

`greetingButton` is used as string for the type of the element, which results in a `greetingButton` HTML tag that the browser don't understand.

So let's change our `greetingButton` to be a React Component:

```javascript
const rootNode = document.getElementById("root")

const GreetingButton = (props) => (
  <button onClick={() => console.log(props.message)}>{props.children}</button>
)

// Container div
const element = (
  <div id="container">
    <GreetingButton message="Hi !">Say Hi</GreetingButton>
    <p className="welcome-text">
      Welcome, <strong>dear user</strong> !
    </p>
    {/** This is functionnaly equivalent to the other GreetingButton */}
    <GreetingButton message="Bye !" children="Say Bye" />
  </div>
)

// Render the paragraph inside the root node
ReactDOM.render(element, rootNode)
```

Starting to look familiar ? 😉 Let's take a look at the compiled code:

```javascript
var rootNode = document.getElementById("root")

var GreetingButton = function GreetingButton(props) {
  return React.createElement(
    "button",
    {
      onClick: function onClick() {
        return console.log(props.message)
      },
    },
    props.children
  )
}

// Container div
var element = React.createElement(
  "div",
  { id: "container" },
  React.createElement(GreetingButton, { message: "Hi !" }, "Say Hi"),
  React.createElement(
    "p",
    { className: "welcome-text" },
    "Welcome, ",
    React.createElement("strong", null, "dear user"),
    " !"
  ),
  React.createElement(GreetingButton, { message: "Bye !" }, "Say Bye")
)

// Render the paragraph inside the root node
ReactDOM.render(element, rootNode)
```

We can see that our component is now used as the **type** for `React.createElement`, which is much better. At render time, our component (= function) will be called and the returned JSX will be injected in the DOM:

```html
<div id="root">
  <div id="container">
    <button>Say Hi</button>
    <p class="welcome-text">Welcome, <strong>dear user</strong> !</p>
    <button>Say Bye</button>
  </div>
</div>
```

So, however you write your React Component, at the end of the day, it's just a function that returns JSX and it all gets compiled down to `React.createElement`:

```javascript
const GreetingButton = (props) => (
  <button onClick={() => console.log(props.message)}>{props.children}</button>
)

// Same result, different writing:
function GreetingButton({ message, children }) {
  return <button onClick={() => console.log(message)}>{children}</button>
}
```

## Conclusion

I hope you learned a few things by reading this post. I think this is really interesting to know what's going on "under the hood" when writing React Components. The more you can compile down JSX in your head, the more efficient you will be using it. Feel free to play around in the [Babel playground](https://babeljs.io/repl#?browsers=&build=&builtIns=false&spec=false&loose=false&code_lz=DwEwlgbgfAEgpgGwQewAQHdkCcEmAenGgG4g&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=true&fileSize=false&timeTravel=false&sourceType=module&lineWrap=false&presets=react&prettier=true&targets=&version=7.9.0&externalPlugins=) to see what's the output of the JSX you write in real-time!

This post was inspired by this great article by Kent C. Dodds: [What is JSX?](https://kentcdodds.com/blog/what-is-jsx)