Backbone.js Templates with Underscore
You have a model full of data and a view that needs to show it. The tempting first move is to build the HTML with string concatenation, and within a day that turns into a tangle of plus signs and quotes that nobody wants to touch. Templates are the way out: you write the markup once, with little slots where the data goes, and let a function fill them in. In Backbone, that function almost always comes from Underscore.
This guide is code-first and builds on the view lifecycle, since a template is simply what goes inside render. We will cover the three template tags, where templates should live, how to feed them model data, the escaping rule that keeps you safe, and how to compile once for speed.
What you'll learn
Backbone leaves templating to you
Backbone deliberately ships no template engine. That sounds like a gap, but it is the same flexibility that lets the library stay small: you pick how data becomes markup. Because Underscore is already a dependency, its _.template is the path of least resistance, and it is what nearly every Backbone codebase reaches for.
Why there is no built-in engine
var greet = _.template('Hello, <%= name %>!');
console.log(greet({ name: 'Ada' }));
// => Hello, Ada!
_.template takes a string with slots and returns a function. Call that function with an object and you get a finished string back. Nothing about it is Backbone-specific, which is exactly the point: if you prefer Handlebars or Mustache, you swap the engine and the rest of your view stays the same.
The three Underscore template tags
Underscore templates have three kinds of slot, and knowing which is which removes most confusion. Two of them print a value; the third runs JavaScript without printing anything.
Interpolate, escape, and evaluate
<%= value %> prints the value as-is (raw)
<%- value %> prints the value HTML-escaped (safe)
<% if (ok) { %> ... <% } %> runs logic, prints nothing
The first prints raw, the second prints an escaped copy, and the third is for control flow such as if and loops. A useful habit: treat the escaping tag as your default for data, and use the raw tag only when you genuinely mean to inject HTML you control.
Where your templates live
A one-line template can sit in your JavaScript, but anything real wants its own home. The classic Backbone approach is a script block with a non-executable type, which the browser ignores as code but hands you as text.
Using a script template block
<script type="text/template" id="row-tpl">
<li class="todo"><%- title %></li>
</script>
You read that markup with $('#row-tpl').html() and pass it to _.template. The browser never tries to run the contents, the markup keeps its indentation, and your HTML stays out of your JavaScript files. For larger apps, the same idea moves into separate template files bundled at build time.
Feeding data from a model
A template function expects a plain object, and a Backbone model hands you one with toJSON. That single call is the bridge between the data layer and the markup.
Looping over a collection in a template
<ul>
<% _.each(items, function (item) { %>
<li><%- item.title %></li>
<% }); %>
</ul>
Here the evaluate tag drives an _.each loop while the escaping tag prints each title safely. This is perfect for a static list. The moment each row needs its own click handling or state, prefer a child view per model instead, rendering each one as covered in the lifecycle guide.
Escaping and XSS: escape by default
This is the section that protects your users, so it earns extra attention. The raw tag prints exactly what it is given, which means a value that contains markup becomes live markup, including a script that runs.
When to use the escape tag instead of the raw tag
// suppose bio came from a user:
// bio = '<img src=x onerror=alert(1)>'
<%= bio %> DANGER: the tag is injected and the script runs
<%- bio %> SAFE: the value is shown as harmless text
The rule is short: escape anything a person or an external system could influence, and use the raw tag only for markup you authored yourself. Getting this backwards is one of the most common ways a front end ends up with a cross-site scripting hole.
Compile once, render many
Compiling a template parses its string into a function, and doing that on every render is wasted work. Compile each template once, then reuse the returned function as many times as you like.
Precompiling for performance
// compile once when the module loads
var rowTpl = _.template($('#row-tpl').html());
var Row = Backbone.View.extend({
tagName: 'li',
render: function () {
this.$el.html(rowTpl(this.model.toJSON()));
return this;
}
});
Now render only fills the slots; the parsing happened a single time. For a list of hundreds of rows that difference is real. If you ever need to change the slot syntax, _.templateSettings lets you redefine the tags globally, though the defaults serve almost everyone.
That is templating in Backbone: pick an engine, learn three tags, keep the markup in its own block, feed it model data, escape what you do not trust, and compile once. With render now producing clean HTML, the next foundations topic, sync, is about where that model data comes from and how it gets back to the server.
Frequently Asked Questions
Does Backbone come with a template engine?
No. Backbone leaves rendering to you and, by convention, pairs with Underscore's _.template, which ships alongside it. You can swap in Handlebars, Mustache, or any engine by overriding how your views turn data into HTML.
What is the difference between the interpolate and escape tags in Underscore templates?
The interpolate tag prints a value exactly as given, so any HTML in it is inserted as markup. The escape tag converts characters like the angle brackets and ampersand to entities first, so the value is shown as plain text. Use escape for anything a user can influence.
How do I render a list or collection in a Backbone template?
Either loop inside the template with an evaluate block calling _.each over the data, or render one child view per model and append each into a parent. For static lists the loop is simplest; for rows with their own behaviour, child views scale better.
Are Underscore templates safe from XSS?
Only when you escape untrusted data. The interpolate tag is raw and will inject whatever it is given, including a script or image tag. Use the escape tag for any value that came from a user or an external source, and reserve the raw tag for trusted markup.
Read next: Backbone.js Sync and the Server, the next foundations guide, or revisit What is a Collection? to see the data a list template loops over.
Put the pieces together
Templates feed your views. The complete guide shows how Models, Views, Routers, and Collections combine into one application.
Explore the Backbone Guide →