Last Updated: February 25, 2016
· plukevdh

Use Handlebars.js with Backbone.js

class Mustachio extends Backbone.View
  render: (template_id, context) ->
    @templates[template_id].call @, context

  @prepare = ->
    templates = {}

    $('script[type="text/x-handlebars-template"]').each ->
      raw_template = this.textContent
      raw_template = this.innerHTML unless raw_template   # IE 8

      templates[] = Handlebars.compile(raw_template)

    @::templates = templates

window.Mustachio = Mustachio

$ ->

Let's dissect.

Mustachio.prepare() crawls the page, finds all the handlebars templates, compiles and caches them. This allows render to simply do a lookup and then call the template with the context.

With your views, you can extend the Mustachio class:

class IdeaList extends Mustachio
    "click .delete": 'delete'


    html = super('idea-item', context)

    @options.parent.$el.append html

Since Mustachio is the parent class, calling super looks up the cached template, renders it and returns the rendered HTML for use. Alternatively, if there is nothing else you need to do, just omit the render method from your model and just allow render to be called on the parent class.

@tjsingleton recommended one additional performance gain for the page load. Instead of crawling and compiling at page load, lazy compile the templates on first request. This would look like the following (with duplicate code removed for brevity):

class Mustachio extends Backbone.View
  templates: {}

  lazyCompileFactory: (template_id, raw_template) ->
    @templates[template_id] = (context) =>
      compiled_template = Handlebars.compile(raw_template)
      @templates[] = compiled_template

  @prepare = ->
    $('script[type="text/x-handlebars-template"]').each (i, item) =>
      raw_template = item.textContent
      raw_template = item.innerHTML unless raw_template   # IE 8

      @::lazyCompileFactory(, raw_template)

Here's the two versions in a gist for easier comparison: