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.
- 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
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
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
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 a renders to a DOM node on the client, it’s going to take a little more work.
- JSX also transpiles
<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
...childrento contain nested arrays, which can be tricky to loop over.
- We also need to think about
Fragmentsupport, 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:
Build JSX structures, then decide at runtime which pragma you want to use to render them. - krakenjs/jsx-pragmatic
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
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.
Go forth and build interesting things with JSX! What else could JSX be used to render?