How the JSX Transform Works


JSX looks like markup in your JavaScript. It's not, and here's how the transform works.

JSX Output

JSX is a superset of JS that allows you to write what looks like markup in your JavaScript code. It's often used for UI coding, because it looks like you can define HTML UIs, such as in React:

import React from 'react'
const SomeUi = _ =>
  <div>
    <h1>Yay, JSX!</h1>
  </div>

Not HTML

But JSX is not runnable inside today's browsers. It must be compiled into JavaScript that the browser understands. And that's great, because JSX was never intended to be HTML. It just has the familiar look of markup for creating UIs.

In React's case, the library uses a virtual DOM to create an in-memory representation of the DOM, or what the document will look like in the browser. To create these virtual DOM elements, react has a React.createElement function that it calls.

JSX Transformation

You can use many tools to compile your JavaScript. A popular choice is Babel. There is a transform in Babel, called babel-plugin-transform-react-jsx that does the JSX transform.

Tag Name and Strings

When run it on JSX code, it'll transform this:

<h1>Yay!</h1>

Into this:

React.createElement('h1', null, 'Yay!')

JSX turns into plain JavaScript function calls.

Attributes

Markup attributes are also turned into props objects, from this:

<h1 className="wowzers">Yay!</h1>

Into this:

React.createElement('h1', { className: 'wowzers' }, 'Yay!')

Nested Tags

And nested children are turned from this:

<div>
  <h1>Yay!</h1>
</div>

Into this:

React.createElement('div', null,
  React.createElement('h1', null, 'Yay!')
)

These are the functions that React uses to build up the virtual DOM.

JSX Pragma

How does the plugin know what to transform the JSX into? It needs a pragma, or directive on how the compiler should treat the file. The default pragma for this babel plugin is React.createElement, which means that all JSX will turn into calls to this function.

This is why, if you forget to import React from 'react', you'll get something like:

Uncaught ReferenceError: React is not defined

Custom Pragma

If you're not using React, but you'd still like a JSX transform on your source files, you can use your own pragma. Let's say instead of React, you were using a UI library called Wakt, and it had a createElement function. You would need to pass an option to engage this custom behavior to the babel plugin via your .babelrc file:

{
  "plugins": [
    ["transform-react-jsx", { "pragma": "wakt.createElement" }]
  ]
}

Now, when compiled, this (imports included for clarity):

import wakt from 'wakt'

<h1>Yay!</h1>

Will be transformed into this:

import wakt from 'wakt'

wakt.createElement('h1', null, 'Yay!')

What are some other cool facts about the JSX transform that you've learned? Or what other applications for it have you found for it?