Replacing JSX with vanilla CoffeeScript

Update: I created a npm package out of the trick found in this post, you can find it here: https://github.com/kalasjocke/react-coffee-elements

At Tictail we tend to build web applications in JavaScript that consume REST APIs. In the past we’ve been heavy users of Backbone.js, though lately we’ve been exploring React.js.

A core component of React.js is their template replacement called JSX. JSX makes it super simple to inline markup in your components. However, it doesn’t play very well with CoffeeScript.

If you search the internet for how to use JSX with CoffeeScript you’ll probably end up on React & CoffeeScript. I did, so I started writing my tags just as vjeux described:

h1 {}, 'Tictail'

However, there has been this one thing that always troubled me with that particular syntax. It’s super expressive and all, but not as short as it could be. You have to pass along an empty options object to match the signature of the React.DOM.tag. After some experimentation it turns out that you can get rid of the options object with some CoffeeScript magic:

h1 = (options...) -> 
options.unshift {} if options[0]['_isReactElement'] or options[0].constructor isnt Object
React.DOM.h1.apply @, options

How handy! Now we just need to figure out a way to make this happen for all React.DOM tags. Let’s generalize into a build_tag function:

build_tag = (tag) ->
(options...) ->
options.unshift {} if options[0]['_isReactElement'] or options[0].constructor isnt Object
React.DOM[tag].apply @, options

And now we just need to do it for every React.DOM element:

DOM = (->
object = {}
for element in Object.keys(React.DOM)
object[element] = build_tag element
object
)()

Now we can use DOM in the same way as we usually use React.DOM:

{h1, h2, h3, div, p} = DOM
h1 className: 'foo', 'Bar'
h2 'Baz'