JSX is a stellar invention, even with React out of the picture.

If you’ve worked with React — and even if you haven’t — you’ve probably heard of JSX. It’s that weird html-in-javascript construct that makes everyone cringe when they first discover it, but that most people love as they come to terms with the idea.

Why is this awesome? Because it embraces the idea that templates are just functions. They take data as input, and they output a structured, visual representation of that data.

Programming languages like javascript are good at handling functions. So naturally, templates are best suited to live directly in the code itself, rather than as separate things with different rules and different conventions. Practically speaking, that brings a myriad of advantages to the table:

  • Regular javascript scoping rules apply; if it’s in scope, you can include it in the template.
  • Regular javascript logic applies; no need to re-invent ‘if’ and ‘else’ in yet another templating language.
  • Template rendering errors are just regular javascript exceptions, with full stack traces.
  • If you’re using static typing, your templates are now type-safe too!

In React, the output from these templating functions is a virtual DOM object, which is eventually mapped onto the real DOM, in order to display content and user interfaces.

But the React team made an incredibly smart move when they came up with JSX:

They didn’t tightly couple JSX to React. Not even a little bit.

Let’s take a step back. What does JSX look like after it’s been compiled?

Looks pretty tightly coupled to me, right? What about all of those React.createElement calls?

But wait! What if we quickly add a @jsx pragma comment?

Babel is smart enough to start transpiling the JSX we wrote to a totally different pragma! In this case, instead of React.createElement it’s now transpiling to zomboComponent.

Now we’ve taken React out of the picture entirely, and we can define our own custom JSX rendering path, and render whatever we like!

For example, maybe we want to render directly to a DOM element instead. We can do that! All we need to do is declare a function which takes name, props and children and have it construct those elements for us:

Now all I need to do is add a /* @jsx renderDom */ comment, and I’m good to go!

Is it really that easy?

Yes, and no. You can absolutely do what I just demonstrated above. But as always, there are a few pesky nuances to consider:

  • With this approach, you have to decide at build-time what pragma the JSX code should be transpiled to use. So — if you want a function that renders html on the server, and that renders to a DOM node on the client, it’s going to take a little more work.
  • JSX also transpiles <MyComponent> and <mycomponent> differently, based on the capitalization of the component name. So a production-ready version of this would need to account for both of those types.
  • There are also plenty of opportunities for ...children to contain nested arrays, which can be tricky to loop over.
  • We also need to think about Fragment support, which now even has its own special <> </> syntax in jsx.

I threw together a small (3kb gzipped) module to make all of the above easier to deal with:

Using jsx-pragmatic, you can decide at run-time, based on the context, exactly how you want to render a component:

The module comes with built-in HTML, DOM, and React renderers.

But you can also easily build your own renderer and plug it in! Renderers in jsx-pragmatic are just functions; and you won’t even have to think about dealing with nested arrays, fragments, and handling the difference between elements like <foo /> versus components like <Bar />. That’s all taken care of for you.

Why not just use React?

At PayPal, we’re building SDKs which render UI directly on third-party sites. We don’t have the advantage of being able to load an entire view library like React on sites we don’t own. But we do love JSX. With it, we can build templates without having to ship a huge templating library — because they’re all transpiled at build time — and we can render them however we want on the web using libraries like jsx-pragmatic.

We use React for apps that we own; but when it comes to building modules to embed directly into our merchant’s pages, we feel that JSX alone gives us most of the power, control and speed we need.

Shout out to Syr!

If you’re interested in a different application of JSX at PayPal, also check out Syr! We’re using Syr to bridge the gap between iOS, Android and web, using the power of cross-platform JSX.

JSX Rules!

Go forth and build interesting things with JSX! What else could JSX be used to render?

works for PayPal, as a lead engineer in Checkout. Opinions expressed herein belong to him and not his employer. daniel@bluesuncorp.co.uk