Handlebars

« Return to Our Notebook

Handlebars

Picking a templating engine

Choosing a templating engine can be a difficult aspect of building a website. Designers need enough control over the structure and layout of the site to be able to refactor it without requiring any programming, while developers need to be able to easily insert data programmatically into the site. Finding the right balance between those needs isn't a trivial task, since it depends a lot on the makeup of your team.

The templating engines that exist span a wide range of organizational styles, from entirely developer-focused to entirely designer-focused, with all kinds of options in between. One of the options that has provided us with the best compromise between ease of use for developers and designers so far has been Handlebars.

What is Handlebars like?

In addition to basic variable substitution, Handlebars provides a limited amount of logic, including simple conditionals, loops, and helper functions. It explicitly does not provide any kind of macro capability, or really any kind of programming language at all - if you need to provide functionality that requires actual logic, you should write it in your host programming language and expose it as a helper function (usually just called a "helper" in Handlebars) to your template.

This is an example of Handlebars syntax, taken from the Handlebars.js home page:

<div class="post">
  <h1>By {{fullName author}}</h1>
  <div class="body">{{body}}</div>

  <h1>Comments</h1>

  {{#each comments}}
  <h2>By {{fullName author}}</h2>
  <div class="body">{{body}}</div>
  {{/each}}
</div>

As you can see, all template directives are wrapped in {{/}} pairs. Several different things can be included inside the braces:

  • A single variable name inside the braces does simple variable substitution. In this example, {{body}} results in the contents of the variable named body in the current context being inserted into the output. By default, the context is just the list of variables that were provided when the template was rendered, but it can be changed by various other directives, as described below.
  • Helpers are called by including the helper name, optionally followed by any arguments (which can be variables). {{fullName author}} inserts the result of calling the fullName helper with the author variable as an argument.
  • Block helpers are introduced by a leading # character. They pass all of the text until the block is ended (by a directive consisting of a / followed by the helper name) to the helper as an extra argument. This allows for structures like loops or conditionals - for instance, {{#each comments}} evaluates the block between the #each and /each once for each element in the comments variable, which is expected to be an array. The context for that evaluation is set to be the array element, so {{body}} in this case is the body of the comment, rather than the body of the post.

Handlebars has several other useful features, but this is enough to give a decent overview. For more details on additional features, see the website for Handlebars.js, which is the canonical implementation.

Why did we choose Handlebars?

(Mostly) logicless templates

It's very easy to start including a lot of logic in your templates as they grow. This is bad for maintainability because template languages are typically not designed for general purpose programming, so code written in your template language isn't going to be as easy to understand as code written in a real programming language. Also, the debugging facilities tend to be less powerful - simple things like stack traces can become incomprehensible or impossible if the code is in a template.

On the other hand, some level of logic is actually necessary to avoid excessively duplicating code. If loops and conditionals didn't exist, the only option would be to move that logic into the site's code. In order to access the HTML to loop over, we'd then have to either split our templates up into tiny chunks (which is bad for maintainability), or generate the HTML in code (which defeats the purpose of using a templating language).

Helpers are also important, because they allow you to pass just the raw data into your template, and then process it for display however you want. This means that your controller code can be much simpler and less likely to require changes. Keeping your controllers as thin as possible tends to be good for maintainability.

Portability

At Infinity, we developed a web application which rendered Handlebars templates entirely in the browser. Eventually we had to migrate some of that template processing over to the server. Rather than port our templates to a more traditional Perl templating language (and then have to maintain templates in two different templating languages), we decided to port Handlebars itself to Perl. To paraphrase Larry, "It's easier to port a templating system than a template."

Because we ported Handlebars to Xslate, we were able to both keep costs low for the client and inherit many of the great features of Xslate like caching, lightning quick template rendering, and excellent error reporting.

Handlebars also has many other implementations for many other languages. In addition to the JavaScript and Perl implementations we've mentioned so far, implementations also exist for languages like Python, Ruby, and Scala, among others. This makes it very easy to share templates between your front and back end when developing web applications regardless of which languages you are using, which significantly increases your flexibility.

Other options

Template::Toolkit

Template::Toolkit has long been the go-to template implementation for Perl. It's very widely used, and has a lot of features. The error handling does leave a lot to be desired though (the contextless Could not render template: undef error is a particularly egregious example), and the syntax is a bit weird (for instance, the string concatenation operator is _). Its power also allows you to build huge masses of unmaintainable macros, if you don't make an effort to keep your templates simple (we have more than one codebase here which suffers from this problem). The thing that bothers me most, though, is the way that Template::Toolkit handles lists. Instead of providing a way of declaring whether a variable contains a scalar or a list, it tries to guess and is wrong a lot, especially when dealing with function return values. These issues, along with beginning to develop more JavaScript-heavy apps, are what initially prompted us to start looking into alternatives.

Xslate

Text::Xslate is a newer templating engine, which fixes many of the problems mentioned above with Template::Toolkit. It still provides the same amount of power as Template::Toolkit, but uses a real parser (using the Top Down Operator Precedence model), which can provide much better error messages at both compile time and runtime. It is also significantly faster than Template::Toolkit. Its most useful feature, though, is that it is built as a compiler targeting a custom VM. This is how it gets most of its speed benefits (templates are automatically compiled at first use, so that at runtime, all it has to do is execute the bytecode), but it also means that new compilers can be written that also target the same bytecode. Text::Handlebars is actually written as a custom parser on top of the Xslate engine. We ended up going with Handlebars over one of the default Xslate syntaxes because of its simplicity and JavaScript compatibility.

Mustache

Handlebars was originally written as a superset of the Mustache templating language. The main difference between the two is that Mustache doesn't provide any support for predefined helper functions - the only features it supports are variables, conditionals, and loops. The only dynamic feature it has is the ability to execute functions passed in as part of the data to be rendered, and that function can only receive a block of template text. The goal was to keep all logic out of templates, but we found this to be too restrictive. A small amount of logic allows us to simplify our templates quite a bit without going far enough to make them unmaintainable. Handlebars also provides a bit more readability compared to Mustache. For instance, {{#comments}}...{{/comments}} actually implicitly does the same thing as {{#each comments}}...{{/each}} (assuming comments is an array variable), but the version with each is more explicit, so is easier to understand.

We solve problems with technology. What can we solve for you?

Reach Out

t: 800.646.0188