# Exploring How React Converts JSX Markup to HTML Code

%%[kempo] 

Ever wonder how React converts JSX markup into HTML code?

Your first thought might be that React is doing something magical behind the scenes, but the truth is a little less interesting. The reality is that React doesn't directly convert JSX into HTML.

## A build tool like Babel or TypeScript converts JSX into function calls

Most React applications that you run in the browser need to be built. A build tool like **Vite** with the [react template](https://vitejs.dev/guide/#scaffolding-your-first-vite-project) can automatically set this up for you. Tools take advantage of this build step to convert JSX into function calls that the browser can properly execute.

Depending on your app's version of React and your build tool, the function that's used may vary. For this post, we'll stick with the function `React.createElement` because it's an API that you can also use directly in your application code.

You can check out the React blog to [learn more about the newer JSX transform and the reasons behind it](https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html).

Ok!

So back to `React.createElement`.

Let's look at an example.

```typescript
function MyComponent() {
    return <div id="my-id">My Component</div>;
}

// The code above gets converted to:
function MyComponent() {
    return React.createElement(
        'div',
        { id: 'my-id' },
        'My Component'
    );
}
```

Each instance of JSX (i.e. `<div></div>`) is converted into a function call like the above. Here's the same example shown in the [babel repl](https://babeljs.io/repl#?browsers=defaults%2C%20not%20ie%2011%2C%20not%20ie_mob%2011&build=&builtIns=false&corejs=3.21&spec=false&loose=false&code_lz=GYVwdgxgLglg9mABAWQJ4GE4FsAOCCmYUAFAJSIDeAUIrYgE75Qj1IA8AJjAG6IwcBeAERZUAWn5CAfGkSZcBImwD0XblIDcVAL5A&debug=false&forceAllTransforms=false&modules=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=env%2Creact%2Cstage-2&prettier=false&targets=&version=7.21.2&externalPlugins=&assumptions=%7B%7D). Once all the JSX is converted, the browser won't have any trouble running the code because it's just executing functions.

Ok great. But what the heck does `createElement` return and how does *that* get turned into HTML?

Great question.

Here's what the `React.createElement` call from above gets turned into.

```typescript
// The createElement function returns the following object
const myElement = {
    'type': 'div',
    'key': null,
    'ref': null,
    'props': {
        'id': 'my-id',
        'children': 'My Component'
    },
    '_owner': null,
    '_store': {}
}
```

The `createElement` function returns an object that describes a React element. The first argument is set to the `type`, the properties in the second argument are stored in the `props` and the remaining arguments are stored in the `props` under the `children` property.

That last bit about the *remaining* arguments is a bit tricky.

**React components can have multiple children that could be nested.**

Here's a more complex example that shows what that might look like:

```typescript
function MyComponent() {
    return <div id="my-id">My <span>Cool</span> Component</div>;
}

// The jsx above gets converted to:
React.createElement(
    'div',
    { id: 'my-id' },
    'My ',
    React.createElement('span', null, 'Cool'),
    ' Component'
);

// The createElement function returns the following object
const myElement = {
    'type': 'div',
    'key': null,
    'ref': null,
    'props': {
        'id': 'my-id',
        'children': [
            'My ',
            {
                'type': 'span',
                'key': null,
                'ref': null,
                'props': {
                    'children': 'Cool'
                },
                '_owner': null,
                '_store': {}
            },
            ' Component'
        ]
    },
    '_owner': null,
    '_store': {}
}
```

You can see that the `children` property in the `div` element's `props` is an array this time because there are multiple children. Also, one of the items in the `children` array is a nested object that describes the `span` element.

## React can convert these object descriptors into HTML

What you're left with is a series of nested objects that describe the HTML. We can take these objects and use standard browser DOM APIs to handle the creation of these elements.

The code to do this is a bit complex because React has to handle updates, account for edge cases, handle performance concerns, and implement additional features to improve the developer experience.

That said, a very basic implementation can be written to help get the point across.

Why don't you try to see if you can figure it out below?

The solution is provided if you get stuck.

Here are some API docs that may help:

* [createTextNode](https://developer.mozilla.org/en-US/docs/Web/API/Document/createTextNode)
    
* [createElement](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement)
    
* [appendChild](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild)
    
* [setAttribute](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute)
    

<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiUmVhY3QgSlNYIHRvIEhUTUwiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zcmMvaW5kZXguanMiOnsiY29kZSI6ImNvbnN0IG15RWxlbWVudCA9IHtcbiAgdHlwZTogJ2RpdicsXG4gIHByb3BzOiB7XG4gICAgc3R5bGU6ICdkaXNwbGF5OiBmbGV4OyBmbGV4LWRpcmVjdGlvbjogY29sdW1uOyBnYXA6IDE2cHg7JyxcbiAgICBjaGlsZHJlbjogW1xuICAgICAgJ1RleHQgQ2hpbGQnLFxuICAgICAge1xuICAgICAgICB0eXBlOiAnYScsXG4gICAgICAgIHByb3BzOiB7XG4gICAgICAgICAgaHJlZjogJ2h0dHBzOi8vYmxvZy50aGVzc2hndXkuY29tLycsXG4gICAgICAgICAgY2hpbGRyZW46ICdCbG9nJ1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAge1xuICAgICAgICB0eXBlOiAnYScsXG4gICAgICAgIHByb3BzOiB7XG4gICAgICAgICAgaHJlZjogJ2h0dHBzOi8vdHdpdHRlci5jb20vVGhlU1NIR3V5JyxcbiAgICAgICAgICBjaGlsZHJlbjogJ1R3aXR0ZXInXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICBdXG4gIH1cbn07XG5cbmZ1bmN0aW9uIHJlbmRlcihlbGVtZW50T2JqKSB7XG4gIC8vIFRPRE86IENyZWF0ZSBhIHJlY3Vyc2l2ZSBmdW5jdGlvbiB0aGF0IGJ1aWxkcyBIVE1MIGVsZW1lbnRzXG4gIHJldHVybiBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZSgnUkVQTEFDRSBNRSEnKTtcbn1cblxuY29uc3Qgcm9vdCA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJyNhcHAnKTtcbmNvbnN0IGNoaWxkRWxlbWVudCA9IHJlbmRlcihteUVsZW1lbnQpO1xucm9vdC5hcHBlbmRDaGlsZChjaGlsZEVsZW1lbnQpO1xuIn0sIi9zcmMvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xufVxuICAgICAgIn0sIi9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sPlxuXG48aGVhZD5cbiAgPHRpdGxlPlBhcmNlbCBTYW5kYm94PC90aXRsZT5cbiAgPG1ldGEgY2hhcnNldD1cIlVURi04XCIgLz5cbjwvaGVhZD5cblxuPGJvZHk+XG4gIDxkaXYgaWQ9XCJhcHBcIj48L2Rpdj5cblxuICA8c2NyaXB0IHNyYz1cInNyYy9pbmRleC5qc1wiPlxuICA8L3NjcmlwdD5cbjwvYm9keT5cblxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHt9LFxuICBcIm1haW5cIjogXCIvc3JjL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In0sIi9zcmMvc29sdXRpb24uanMiOnsiY29kZSI6ImNvbnN0IG15RWxlbWVudCA9IHtcbiAgdHlwZTogJ2RpdicsXG4gIHByb3BzOiB7XG4gICAgc3R5bGU6ICdkaXNwbGF5OiBmbGV4OyBmbGV4LWRpcmVjdGlvbjogY29sdW1uOyBnYXA6IDE2cHg7JyxcbiAgICBjaGlsZHJlbjogW1xuICAgICAgJ1RleHQgQ2hpbGQnLFxuICAgICAge1xuICAgICAgICB0eXBlOiAnYScsXG4gICAgICAgIHByb3BzOiB7XG4gICAgICAgICAgaHJlZjogJ2h0dHBzOi8vYmxvZy50aGVzc2hndXkuY29tLycsXG4gICAgICAgICAgY2hpbGRyZW46ICdCbG9nJ1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAge1xuICAgICAgICB0eXBlOiAnYScsXG4gICAgICAgIHByb3BzOiB7XG4gICAgICAgICAgaHJlZjogJ2h0dHBzOi8vdHdpdHRlci5jb20vVGhlU1NIR3V5JyxcbiAgICAgICAgICBjaGlsZHJlbjogJ1R3aXR0ZXInXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICBdXG4gIH1cbn07XG5cbmZ1bmN0aW9uIHJlbmRlcihlbGVtZW50T2JqKSB7XG4gIGlmICh0eXBlb2YgZWxlbWVudE9iaiA9PT0gJ3N0cmluZycpIHJldHVybiBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShlbGVtZW50T2JqKTtcblxuICBjb25zdCBlbGVtZW50ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChlbGVtZW50T2JqLnR5cGUpO1xuICBjb25zdCB7IGNoaWxkcmVuLCAuLi5lbGVtZW50QXR0cnMgfSA9IGVsZW1lbnRPYmoucHJvcHM7XG5cbiAgZm9yIChjb25zdCBrZXkgaW4gZWxlbWVudEF0dHJzKSB7XG4gICAgY29uc3QgdmFsdWUgPSBlbGVtZW50QXR0cnNba2V5XTtcbiAgICBlbGVtZW50LnNldEF0dHJpYnV0ZShrZXksIHZhbHVlKTtcbiAgfVxuXG4gIGlmIChBcnJheS5pc0FycmF5KGNoaWxkcmVuKSkge1xuICAgIGNoaWxkcmVuLmZvckVhY2goY2hpbGQgPT4ge1xuICAgICAgZWxlbWVudC5hcHBlbmRDaGlsZChyZW5kZXIoY2hpbGQpKTtcbiAgICB9KTtcbiAgfSBlbHNlIHtcbiAgICBlbGVtZW50LmFwcGVuZENoaWxkKHJlbmRlcihjaGlsZHJlbikpO1xuICB9XG5cbiAgcmV0dXJuIGVsZW1lbnQ7XG59XG5cbmNvbnN0IHJvb3QgPSBkb2N1bWVudC5xdWVyeVNlbGVjdG9yKCcjYXBwJyk7XG5jb25zdCBjaGlsZEVsZW1lbnQgPSByZW5kZXIobXlFbGVtZW50KTtcbnJvb3QuYXBwZW5kQ2hpbGQoY2hpbGRFbGVtZW50KTsifX0sInRlbXBsYXRlIjoidmFuaWxsYSJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6ZmFsc2UsImNsb3NhYmxlVGFicyI6dHJ1ZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6dHJ1ZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5Ijp0cnVlfX19LCJnZW5lcmFsIjp7ImFjdGl2ZUZpbGUiOiIvc3JjL2luZGV4LmpzIiwidmlzaWJsZUZpbGVzIjpbIi9zcmMvaW5kZXguanMiLCIvc3JjL3NvbHV0aW9uLmpzIl0sInRoZW1lIjoiZ3J1dmJveERhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nUHJldmlldyciLCJtZWRpdW0iOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="700px" width="100%"></iframe>
