React Internals — JSX transformation to JS and DOM

Pramod Mali
4 min readJul 3, 2021

When I moved to React, for the initial few months I felt why we are writing something like JSX, why can’t we just write HTML instead? Kept wondering that JSX is just mimicking the HTML(I used to work on Angular and AgularJS). Then I started reading about JSX and how it’s not [just] an HTML copycat.

What problems do JSX tries solve?

When we update anything in DOM, it a costly operation. When something is updated, that something may go through a sequence of events and different phases, this takes time and resources.

So, now how does JSX differs from this? Isn’t it updating DOM? The answer is yes, but how it does makes a difference. A JSX goes through a sequence before creating/updating DOM. Many times we hear about React uses something know as Virtual DOM.

How does React solve this problem?

So, does react creates a copy of Physical DOM(what uses sees) and call it Virtual DOM and updates it? The answer is yes and no both. No, because the DOM that the user sees on screen is HTML/XML-like structure, and React doesn’t keep that in form of HTML/XML-like, and Yes because it’s a JS object(React.Element).

Updating objects are much faster than updating DOM with every createupdateunload phase. As developers, we write JSX and React converts to JSX.Element and with the process know as Reconciliation(a different topic itself) and it applies changes to Physical DOM.

What are the steps?

So, let’s understand this by the below example, using typescript just to show actual types.

Simple React component
A simple React component

First, this JSX will go to React.createElement() API. React.createElement API takes three arguments, they are type, props, children. Considering the above example it will be something as follow

JSX to React.createElement
JSX to createElement API
  • The first is type, it could be a string or reference to a functional/class component or Symbol(react.fragment) when using React.Fragment. If it’s an existing HTML tag(i.e. div, span) it’ll be a lowercase string. If it’s a custom component it must start with a capital letter, many times we get an error when we name our component starting with a lowercase character.
  • The second oneprops, it could be an object/null. Any props(custom or inbuilt i.e. ref, children, key) that we provide on a component go here.
  • The third one is children, it’s an optional parameter. This can be a string, another createElement(when using nesting) instance.

Following is an example of a nested component

JSX to createElement API for nested component

One last example before we move to JSX.Element, with our own custom component

Custom JSX component to createElement

Representing createElement() in terms of object

So, let’s go back to the initial example

JSX -> createElement -> JSX.Element

So, finally, React.createElement() returns JSX.Element that looks like an object. Whenever something changes, React tries to compare the previous object with the current and decides what to keep, discard and re-create. Let’s try to understand this JSX.Element

  • $$typeof: this acts as a unique identifier for a subset. Symbols are unique always.
  • type: Similar to what we had in createElement()’s type argument.
  • key: key prop is required when iterating over the list/array.
  • ref: Reference to actual DOM node.
  • props: Similar to createElement()’s props argument, here you’ll notice there is no children property on JSX.Element object, instead it is inside JSX.Element.props
  • There are some other properties which _owner, _self, _source, _store

Let’s also see what happens when there are nested/child components

JSX.Element for nested/child components

And finally, everything is rendered using the ReactDOM.render() method. Playground for JSX to createElement

--

--