React’s JSX: The Other Side of the Coin

When React was released, many people took one look at JSX and lost their minds. What are these angle brackets doing in JavaScript?! What about separation of concerns? Has Facebook learned nothing from the community?

Like many, my initial reaction to React’s JSX was skeptical, to say the least. And while I’ve come to love JSX, anytime I introduce it to a new developer, I feel like I’m showing off my ugly baby.

Try to imagine an ugly baby here. My son is clearly adorable.
Try to imagine an ugly baby here. My son is clearly adorable.

Despite the initial drama, I’ve come to realize that JSX isn’t such a radical idea after all. In fact, it’s simply the other side of the coin. It’s a natural evolutionary transition. To appreciate why, a history lesson is in order…

Phase 1: Unobtrusive JavaScript

Remember the good old days of jQuery? The era of unobtrusive JavaScript was in full bloom. Our HTML was pure HTML. Our JavaScript was pure JavaScript. Our concerns were perfectly separated.

We’d write HTML like this:

<a class=”hide”>Click to hide me</a>

Then we’d write JavaScript like this:

$(.hide’).click(function() { $(this).hide(); } 

#win. Right? Not exactly.

This seemed like a great idea. Our HTML is totally pure! But then we realized some problems: Uh, how can I tell that these two lines are interconnected? Answer: I can’t unless I read every single line of JavaScript. With this pattern, you can’t change a line of markup without checking every single line of JavaScript to assure you’re not breaking a selector. You see, there’s no actual separation going on here. Sure, the JS and HTML are in separate files, but these two technologies are fundamentally joined at the hip. They must move in lockstep or the application will crash.

Strictly separating HTML and JS actually led to applications that were harder to maintain and debug. Each time you wanted to change a line of markup, you had to worry about breaking a jQuery selector. Perhaps if we relaxed our religious devotion to separation of concerns, we could relieve some of this pain? This ushered in phase 2…

Phase 2: Two-way Binding

When front-end developers saw two-way binding in Knockout and Angular, it was a revelation. Many of us tossed our religious devotion to separation of concerns and embraced the power of declaring bindings in HTML. When data changed, the UI changed. When the UI changed, the data changed. So clean. So simple.

Sure, every library and framework has a proprietary way of getting this done, but they’re all fundamentally doing the same thing. Just consider this simple example of iterating over an array in a few popular frameworks:

<div ng-repeat=”user in users”>
{{#each user in users}}
data-bind=”foreach: users”

But something interesting is at play here. Few have recognized a very fundamental problem: We’re effectively putting JavaScript in our HTML. This isn’t separation of concerns. All of these approaches do the same thing: They make HTML more powerful by adding extra proprietary markup. This markup is effectively parsed as JavaScript. And now that we’re finally comfortable intermingling JS and HTML this way, it’s time for React to step in and show us the other side of the coin…

Phase 3: JSX

React’s JSX isn’t a radical shift. It’s merely the fruit of a simple realization:

As an industry, we’ve already decided: HTML and JavaScript belong together.

Admittedly, we didn’t say this out loud. But embracing Angular, Knockout and Ember made our new preference clear. As I established above, writing data-binding code in HTML is effectively putting JS in HTML. But if we’re going to intermingle, why should we choose to augment a technology as weak and lax as HTML? Browsers have loosely interpreted HTML since the beginning of time. So is HTML a logical foundation for declaring data-binding, looping, and conditional logic?

Facebook recognized that JavaScript was a more logical and powerful technology for handling these two intermingled concerns. The epiphany comes down to this:

Angular, Ember and Knockout put “JS” in your HTML.
React puts “HTML” in your JS.

The benefits of this move are multifaceted and not necessarily appreciated until you’ve tried working with React and JSX. React’s JSX is fundamentally superior to all the “Phase 2” style frameworks above for a few simple reasons:

Compile-time Errors

When you make a typo in HTML, you generally have no idea where you screwed up. In many cases it’s a silent run-time error. For example, if you type n-repeat instead of ng-repeat when working with Angular, nothing will happen. Same story with data-bnd vs data-bind in Knockout. In either case, your app will silently fail at runtime. That’s frustrating.

In contrast, when you make a typo in JSX, it won’t compile. Forgot to close that <li> tag? Wouldn’t you love to get rich feedback like this when you make a typo in your HTML?

ReactifyError: /components/header.js: Parse Error: Line 23: Expected corresponding JSX closing tag for li while parsing file: /components/header.js

With JSX, this detailed feedback is finally a reality! It’s hard to overemphasize what a big win this is. This rapid feedback loop greatly increases productivity. As I discuss in my Clean Code course, well- engineered solutions fail fast.

Leverage the Full Power of JavaScript

Composing your markup within JavaScript means you can enjoy all the power of JavaScript when working with your markup, instead of a small proprietary subset that is offered within HTML-centric frameworks like Angular and Knockout.

Client-side frameworks shouldn’t require you to learn a proprietary syntax for declaring loops and conditionals.

React avoids the overhead of learning yet another proprietary way to declare looping and basic conditional logic. As you can see above in the Phase 2 section, every two-way binding framework utilizes its own special syntax. In contrast, JSX looks nearly identical to HTML, and it uses plain ‘ol JavaScript for things like conditionals and loops. In an ecosystem as fragmented as JavaScript, not having to learn yet another proprietary data binding syntax is a nice win.

And since you’re writing your markup in the same file as the associated JavaScript data, many IDE’s will give you intellisense support as you reference your functions. Think about how often you’ve made a typo when referencing a function in HTML-oriented frameworks.

Intellisense support as I reference JavaScript functions in JSX? Nice.

Final Thoughts

JSX isn’t some wild idea. It’s a natural progression. So try not to freak out.

JSX isn’t revolutionary. It’s evolutionary.

Like most forms of evolution, it’s a clear improvement.

Want to learn more? Check out my new course “Building Applications with React and Flux” on Pluralsight.

5 replies on “React’s JSX: The Other Side of the Coin”

  1. JSX evolutionary?
    Back to server generated UI? Back to writing UI by code?
    No, thank you. Definitely devolution.

    The whole reasoning behind the JSX is flawed.

    In your article, you gave the example “of iterating over an array in a few popular frameworks” and come up wit conclusion that:
    “We’re effectively putting JavaScript in our HTML”

    First of all, where’s the Javascript in the Knockout example?
    data-bind=”foreach: users”
    That’s an HTML5 custom attribute. It’s made specifically for the purpose of allowing “proprietary information to be exchanged between the HTML and its DOM representation that may be used by scripts” (from Mozilla’s MDN).

    Second, with Knockout framework, who is forcing you to put JavaScript in your HTML?
    You should never do that because you’re breaking the MVVM concept.
    Some say “oh, but in real world there’s no other way sometimes”. No, there is, look to custom bindings in Knockout for example.
    Alternatively, Knockout also offers inline syntax similar to your Ember example, but that should not be abused and be avoided, it’s not the preferred or right way.

    Back to your example, with creating items based on iterating on a list of items in the model: the for-each in Knockout is a smart way to create and add HTML elements to a parent element.
    It’s an interesting pattern and it’s a solution for the today’s lack of web components. Ideally, there should be an items control where you can set an item template and the model’s property of an observable collection of items.

    About another thing you say:
    “Client-side frameworks shouldn’t require you to learn a proprietary syntax for declaring loops and conditionals.”

    Seriously, are you comparing JSX syntax, API, server setup and flow, with, for example, Knockout?
    You must be kidding me.

    But besides all these, the most important thing we should all realize and understand is that the lack of creating and standardization of new and better ways to develop web apps is the culprit.

    Can the majority agree that data binding is (absolutely) necessary?
    Can we agree that we need custom web components?
    Can we agree that we need controls like a flexbox or grid?
    With just the bare HTML and JS and without frameworks we would be coding like in the 90’s.
    Why can’t web adopt and standardize faster ways which in other development technologies exist for more than a decade?

    There are several signs things are apparently starting to move in the right direction: web components, polymer, Microsoft apparently started to adopt standards more with IE10, IE11, Edge, etc.

    JSX is is doomed and will fail while other frameworks like Knockout for example will continue to stay relevant because of the obvious differences in the way they work.

    1. “where’s the Javascript in the Knockout example?”

      Right here: data-bind=”foreach: users”

      Yes, data- is an HTML5 standard, but it’s the content inside that matters in this discussion. This line is simply a DSL that ends up running a loop in JS. Knockout’s JS looks for the foreach keyword in the data-bind, then runs JS looping logic on the users array. So it’s merely a thin abstraction layer over JS. Same story with Angular and Ember.

      Regarding “Client-side frameworks shouldn’t require you to learn a proprietary syntax for declaring loops and conditionals.”:

      Consider a simple conditional.

      KO requires this:
      <div data-bind=”if: conditionalHere”>

      In React you simply use JS:
      if (conditional)

      I find the latter preferable because it’s plain JS. I can use all the power of JS instead of a the proprietary DSL that Angular, KO, and Ember offer.

      “JSX is is doomed”

      As fast as JS moves, everything we’re using today is ultimately doomed. 🙂

      That said, I’ve built significant apps in KO, Angular, and React. You can certainly build great apps in each. But I currently find working in React most enjoyable and productive. The excellent error messaging and simple API make it easy to compose complex apps. And I believe the pattern of “HTML” in JS is preferable to “JS” in HTML for the reasons I outlined in the article. I agree that web components are an interesting tech (which is why I published a Pluralsight course on them) and I’m excited to see what the future holds.

  2. Right here: data-bind=”foreach: users”

    No, that’s not Javascript.
    Sure, to make it work there’s some Javascript under the hood as you already described.
    But the point is your allegation was
    “We’re effectively putting JavaScript in our HTML”
    which is not correct.

    We’re not breaking any separation of concerns pattern, things stay nicely decoupled as they should.

    And about your other example:

    that’s a bad example. Yes, (unfortunately) Knockout let’s you do that, but you shouldn’t abuse it, instead use some properties in the view-model and bind to those.

    1. Note that I said *effectively*. data-bind, ng-repeat, etc are merely a proxy to JS calls. They’re a DSL that calls JS behind the scenes. In the case of data-bind=”if..” that’s particularly evident.

  3. React is trying to buy everyone into the idea that the typical MVVM simple separation for example is bad and instead mixing and learning (yet another API) from some company is a good thing, just because it’s Facebook.

Comments are closed.