Introducing xūs: A reactive template engine on top of mobx

This post will introduce you to xūs, the templating language I’ve been working on recently which brings the simplicity of mustache and the expressiveness of mobx-state-tree together, and makes laying out reactive user interfaces incredibly easy.

(x-posted from onurgunduz.com)

Recently I came up with an idea I think could save the world. It’s a regular mustache-y template engine which has a lexer, parser and all of that (streaming though) but instead of transforming HTML into regular old HTML again, it compiles them into an observer React component tree. That is to say, given a state object (constructed by mobx or mobx-state-tree or whatever state container implementation that you have with similar characteristics) it will re-render the tree efficiently when something has changed.

Not the most original idea, I know, but it was so tempting to implement something like this on top of mobx, and I am kind of curious about how this could change the way I write UIs.

Here’s an example of a partially finished todo app:

It starts with a template written in xūs/mustache which shows a list of todo items and the number of completed ones:

If you have worked with mustache or any other templating language before, this should be really straightforward.

xūs is so simple, there aren’t many original features yet :) It supports mustache spec with the exception of lambdas and partials.

In mustache, this is a section: {#todos} … {/todos}. The behaviour of the section is determined by the current context. According to the spec, if todos value is of type boolean, then the HTML between the pound and slash will either be displayed or not. If it is a non-empty Array, then it is rendered one or more times. And finally, when it is an object, the value of it is used as the context for a single rendering of the block.

Nested inside the todos section, there is a li tag with title variable in it, and there is also the done section on class attribute which wraps a class name; and then another string, toggle on onclick.

Furthermore, we have completedCount on top where a string will be expanded (just like title) if the key name is found in the current context.

Looking at this template, you can more or less tell what will happen. For example, you can say, finished class will only be assigned if done is set to true. But there is no information here that tells our compiler the type of done, whether it is a boolean or not. So this will only get you so far if you are not running inside a very smart VM, many VMs shouldn’t be that smart anyway. And as for toggle, it could be anything. But you know that it’s just a reference to something else. A reference to an event, or maybe an action name.

So, we need something else that tells us about the data, how to render it and interact with it as well. We need a context.

What is missing? Types of course! Let’s start with them. Let’s focus on a single Todo item first, it should have a title and a done value. So when typed, it looks something like this:

{
title: string
done : boolean
}

Next, we need that toggle action which will negate the value of done:

toggle: function() {
this.done = !this.done
}

And, congratulations! You have just created a mobx-state-tree model:

Let’s create another one.

This time, call it State with a single value of type Array which will hold a list of Todos. Ah, and before I forget, we are creating this model for the purpose of getting the number of completed tasks; so, we also need an accessor for computing that value:

Finally, let’s create a State instance with an initial value:

That’s it! We now have enough information for rendering the layout. With xūs, it is super easy!

xūs exposes a default function which expects a template string and a state object. Using xūsify transform you can just require() your template files and they will be returned as pre-compiled render functions with this signature: function render(state, options)

Using xūsify is faster because you don’t need to parse your templates again. xūsify already does it for you during compilation. This is also the reason why it’s possible to emit very tiny bundles with it, because you don’t have to include parser in the final build. (In fact, it is currently mostly optimized for that!)

See xūs and xūsify readmes for more information about the usage.

Rendering with xūs

Assuming you have saved your layout as layout.html, this is how you require() it as a stand-alone render function:

const render = require("./layout.html")

This looks very similar to a functional React.Component, and actually it is. It expects a state and an options argument which are both required.

Since we don’t use setState anymore, you should translate the state argument to props in your head.

Ok, let’s finish this up!

Given our state, following code block shows how you can create a React-ive component tree and render it into DOM using ReactDOM.render. xūs does not ship with React or mobx, that’s why you need to provide React.createElement and mobx.observer methods in the options object as well:

And, that’s it! Your partial todo application is ready.

Furthermore, it’s not magic! There is so much information there.

See the full example code here.

See the example on CodePen.

See API documentation.

Status

I’d say, at this point, xūs is somewhat web-turing complete. Of course, there is still so much else to do! Optimizations, more tests, adding new language constructs and such. One new addition could be a simple range operator for example, e.g. {#todos:10–20}. Have a look at the Issues page for the planned features.

So, yes, please go ahead and create cool things with it. This is not a competitor to JSX or whatever, but it is improving :)

If you are curious why I’ve started this project and how this could play a role within a larger framework, take a look at “Templates, state trees, a school of ‘whatever’ and a state definition language”.