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')
)
)
)
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 />
becomesReact.createElement(Jack)
-
<jack />
becomesReact.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' />
becomesReact.createElement(Something, {propA: 'a'})
-
<Something propB={b} />
becomesReact.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>
becomesReact.createElement(Something, {}, 'text')
-
<Something><something /><somethingElse /></Something>
becomesReact.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>
becomesReact.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' />
becomesReact.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:
I will send you useful articles, cheatsheets and code.
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!
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.
Awesome, thanks for sharing.