How I learned to stop worrying and love the JSX

So you’ve heard the commotion about React, and thought you’d take a look. And you’ve liked what you’ve seen! Or you did, until you saw this:

<div onClick={this.handleClick}>JSX butters my toast</div>

OK. You’re happy to give a new technology the benefit of the doubt. But this? Didn’t we escape PHP years ago? And what ever happened to separation of concerns?!

But I’m here to reassure you that not everything is as it seems. The above code is not JavaScript-in-HTML. If anything it actually promotes separation of concerns, especially compared to “competitor framework A”. And its best feature?

JSX is optional.

See that code above? It is actually an illusion. This is its true form:

React.createElement(
    'div',
    {onClick: this.handleClick},
    "JSX butters my toast"
)

Not so bad, huh? I mean, the only way you could make this less like JavaScript-in-HTML would be to use document.createElement. Or to rearrange bits on a hard drive platter with a magnetic needle.

But dad jokes aside, you may now be asking why-the-fuck have we invented an entire new language just so we can avoid a couple characters of JavaScript? This is a good question, but if you’ve read my Raw React series, you probably already know the answer. It looks something like this:

var rootElement =
  React.createElement('div', {}, 
    React.createElement('h1', {}, "Contacts"),
    React.createElement('ul', {},
      React.createElement('li', {},
        React.createElement('h2', {}, "James Nelson"),
        React.createElement('a', {href: 'mailto:james@jamesknelson.com'}, 'james@jamesknelson.com')
      ),
      React.createElement('li', {},
        React.createElement('h2', {}, "Joe Citizen"),
        React.createElement('a', {href: 'mailto:joe@example.com'}, 'joe@example.com')
      )
    )
  )
Can you grok the above code? If not, you need to read Raw React. Learning JSX without knowing React.createElement is like learning to drive a semi before you learn to crawl.

Typing this kind of thing out is error-prone and downright painful. Of course, there are solutions.

One is to do this:

var c = React.createElement
var element = c('h1', {}, "I'm so minimalist that")`

And this is a good solution. It saves you keystrokes, it doesn’t require a build system, and it makes you look really smart/hipster. But it is also harder to read, it won’t save you from parenthesis hell, and if you want to write code like this then you should probably learn LISP.

JSX is another solution.

But what is JSX?

JSX is a bunch of shortcuts to make calling React.createElement less painful.

Let me repeat that. JSX is a bunch of shortcuts. That means that unlike PHP, ASP or Angular JS, JSX is not a way of adding control flow to HTML. If you must think about JSX in terms of HTML, it is closer to HTML-in-JavaScript than JavaScript-controlling-HTML. But it isn’t that either. It really is just a bunch of shortcuts for writing React.createElement. In particular:

React.createElement is written with <tags>

The capitalisation of the tag indicates whether you’re creating a ReactElement representing a DOM element, or one representing a custom component:

  • <Jack /> becomes React.createElement(Jack)
  • <jack /> becomes React.createElement('jack')

props look like “attributes”

String attributes are denoted with quotes (just like in HTML), while expressions are denoted with curly braces:

  • <Something propA='a' /> becomes React.createElement(Something, {propA: 'a'})
  • <Something propB={b} /> becomes React.createElement(Something, {propB: b})

An element’s props.children is denoted by the tag’s child nodes

Or in other words, anything inside a tag is passed as the third argument to the corresponding React.createElement call:

  • <Something>text</Something>
    becomes
    React.createElement(Something, {}, 'text')
  • <Something><something /><somethingElse /></Something>
    becomes
    React.createElement(Something, {}, React.createElement('something'), React.createElement('somethingElse'))

{} lets you interpolate within children:

Just like PHP let’s you interpolate with <?php… JSX let’s you interpolate with {}. Just like PHP.

  • <Something>{c}</Something> becomes React.createElement(Something, {}, c)

{...object} lets you merge props

And last (but not least), JSX allows you to add multiple props at once by adding an {...object} inside your attribute list. React will use Object.assign to merge this object with any individual props:

  • <Something {...propsA} propB='B' />
    becomes
    React.createElement(Something, Object.assign({}, propsA, {probB: 'B'}))

Let’s do an exercise

Remember that big nasty tangle of React.createElement from above? Let’s convert it to JSX. Here it is again:

var rootElement =
  React.createElement('div', {}, 
    React.createElement('h1', {}, "Contacts"),
    React.createElement('ul', {},
      React.createElement('li', {},
        React.createElement('h2', {}, "James Nelson"),
        React.createElement('a', {href: 'mailto:james@jamesknelson.com'}, 'james@jamesknelson.com')
      ),
      React.createElement('li', {},
        React.createElement('h2', {}, "Joe Citizen"),
        React.createElement('a', {href: 'mailto:joe@example.com'}, 'joe@example.com')
      )
    )
  )

When you’re done, hover over or touch the box below to see my answer. But only when you’re done. Practice makes perfect.

var rootElement =
    <div>
        <h1>Contacts</h1>
        <ul>
            <li>
                <h2>James Nelson</h2>
                <a href='mailto:james@jamesknelson.com'>james@jamesknelson.com</a>
            </li>
            <li>
                <h2>Joe Citizen</h2>
                <a href='mailto:joe@example.com'>joe@example.com</a>
            </li>
        </ul>
    </div>

And there you have it. JSX is really just five simple rules which make your app’s source cleaner and simpler. Of course, learning the rules takes time. And you don’t want to have to come back to this article every time you want to look up the rules. That is why why subscribers to my newsletter receive a high-res PDF of this handy dandy JSX cheatsheet! But I’m getting ahead of myself – didn’t I mention earlier that we don’t actually need JSX?

Get the PDF cheatsheet!

When you shouldn’t use JSX

For all of the benefits that JSX can have, it is not always appropriate. Here are a few reasons you may want to skip JSX in favour of Raw React:

A build step can be overkill

If you’re building a reasonable size application and you’re going to need a build step anyway, JSX is great. But if you’re building a tiny library which calls React.createElement once or twice? JSX will just add unneeded complexity.

When you’re not writing components

The vast majority of React.createElement calls are made within custom components. JSX feels like it was made for these scenarios – it helps eliminate typos in large tree structures, it eases conversion from HTML mockups to ReactElement trees, and it generally just kicks ass.

But React.createElement isn’t just used in components. In particular, you’ll also need to call React.createElement when bootstrapping your application, often following a pattern like this:

ReactDOM.render(
    React.createElement(Application, { data: store.getState() }),
    'app-id'
)

While you could use JSX here, I’d argue that it only serves to hide what is actually going on – and to add a funny x character to the end of your filename.

When it just doesn’t feel as clear

This is something which will come with experience, but sometimes a piece of code just feels like it shouldn’t be JSX. This sounds like a cop-out, so let’s look at a real example from a higher-order component in one of my applications. The main purpose of the component is to add lifecycle handlers to an existing component.

render() {
    // This feels easier to grok than `<WrappedComponent {...this.props} />`
    return React.createElement(WrappedComponent, this.props)
}

Of course, knowing how and when to use JSX is only like one tenth of the battle. The rest is actually making the damned build process work.

How to turn JSX into JavaScript

If you just want something that works right now, you can skip the build process dance by using the now-deprecated Babel 5 in-browser transform tool. Just add this one simple line to your index.html and behold the power of old software:

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.js"></script>

Of course, those amongst us who need to have something which will actually work for the foreseeable future (or for whom performance matters in the slightest) will need to convert our JSX into JavaScript before it hits the browser. There are two ways to approach this: using the command line, or compiling files as part of a larger build system. Let’s start with the easier of the two.

Compiling JSX on the command line

There are two ways to get babel to compile JSX on the command line. One is to install Babel 5 globally:

npm install babel@5 -g

And then to compile your file using the babel command line interface:

babel filename.jsx --out-file filename.js

Easy as one-two. But I mentioned there are two ways to compile JSX on the command line – and that was only one?

The other way is Babel 6. You’d think that not much would change with a single version number increment, but you’d be wrong. From my article on Babel 6:

Over the last year, Babel has become the go-to tool for transforming ES2015 and JSX into boring old JavaScript. But seemingly overnight, Babel 6 changed everything. The babel package was deprecated, and running babel doesn’t actually transform ES2015 to ES5.

If you’re just learning, I recommend sticking to Babel 5. It is easier. But if you need to use Babel 6, you can find out how at my Introduction to the Babel 6 CLI – just add babel-react-preset in addition to the other presets mentioned in the article.

Of course, compiling every file manually on the command line won’t scale. So if you’re already committed to a serious React/JSX project, you’ll probably want to use Webpack.

Building JSX with Webpack

The heading for this section is a little white lie – Webpack doesn’t actually build JSX. It just delegates the JSX compile step to Babel. And this means that a Webpack build has the same Babel configuration issues as CLI compilation.

If your project is serious enough that Webpack makes sense, you probably want to skip Babel 5, grab some hard liquor, and then read my guide to Using ES6 and ES7 in the Browser, with Babel 6 and Webpack – just add babel-react-preset to the other preset packages I mention to get JSX support in addition to ES6.

Still reading?

All the blog in the world isn’t going to convince you that this weird-looking syntax is really quite loveable. To get to that point, you’re going to need to get out there and write some JSX!

But what should you actually write? Well, the contacts app which you’ll build in my Raw React series would be a great place to start. The series uses pure React.createElement, giving you the opportunity to convert it to JSX yourself (and to use the PDF cheatsheet which subscribers receive).

But Raw React and JSX will only get you so far. A real app still needs structure – for its data store, for its components, and for its file tree. And as it happens, you’re in luck! Because I’ll be publishing guides to each of these in the lead-up to my book on Real React. Get on my newsletter now to make sure you don’t miss it – and to get five free high-res JavaScript and React cheatsheets. Including this JSX one:

JSX cheatsheet

I will send you useful articles, cheatsheets and code.

I won't send you useless inbox filler. No spam, ever.
Unsubscribe at any time.

One more thing – I love hearing your questions, offers and opinions. If you have something to say, leave a comment or send me an e-mail at james@jamesknelson.com. I’m looking forward to hearing from you!

Read More

2 Comments How I learned to stop worrying and love the JSX

  1. Joey Trapp

    Why ever use React.createElement(“div”, null) over React.DOM.div(). Using object destucturing to make vars can be pretty expressive.

    import React from “react”;

    const { div, ul, li } = React.DOM;

    export default function AList(props) {
    return div({ className: “someclass” },
    ul(null,
    props.list.map(i => (
    li(null, i)
    ))));
    }

    Formatting or mistakes possible; commenting on mobile.

    Reply

Leave a Reply

Your email address will not be published.