Template systems in Node

Regardless of your background in web development, you’ve likely used a web template system (engine). The goal of a template system is to process templates (usually in combination with a set of input data) to form finalized web pages.

template(data) => final HTML

Although some engines are designed specifically for HTML output, many can be used to generate any type of a text output.

Node has a rich ecosystem of template systems available. Since it is server-side JavaScript, many of these engines are built to work both on the client and server. The benefit: template reuse in your web applications.

All the template systems mentioned in this article work both client and server side.

In this article, rather than boring you with a module by module synopsis (hint: we’d be here for a while), we will zoom out and look at the types of systems that are available and why you might choose one style over another depending on your needs.

Types of Template Systems

Node’s web template systems can be divided into four general approaches. They are:

  1. Embedded JavaScript
  2. Custom Domain Specific Languages (DSLs)
  3. Logic-less
  4. Programmatic

Embedded JavaScript

Like the style of PHP, JSP, or ERB templates? Prefer working with vanilla JavaScript? If so, take a look at embedded JavaScript. At the core, these engines allow JavaScript code to be evaluated within a template. The most notable is EJS, which looks a lot like PHP or JSP (except it uses JavaScript, of course):

<% if (loggedIn) { %>
<a href="/account"><%= firstName %> <%= lastName %></a>
<% } else { %>
<a href="/login">Log In</a>
<% } %>
  <% records.forEach(function (record, index) { %>
    <li><%=index%>: <%= record.title %></li>
  <% } %>

In addition to embedded JavaScript, EJS templates include extras like partials and filters. One notable usage of EJS templates is the npmjs site (GitHub).

If you only need interpolation and evaluation in your templates (no extras like partials, filters, etc.), check out the micro-templates provided in Underscore/Lo-Dash. There also are embedded CoffeeScript templates.

Custom Markup Languages

Writing vanilla JavaScript templates can get verbose and ugly with <% } %> code sitting all over the place. Here is where the world of custom DSLs comes in. These languages vary widely on syntax. However, you’ll typically end up with cleaner templates and some extra goodies like mixins, filters, and inheritance. Let’s look at a couple examples.

doT takes a minimalistic approach (it’s also built for speed):

{{? it.loggedIn }}
  <a href="/account">{{= it.firstName }} {{= it.lastName }}</a>
  <a href="/login">Log In</a>
  {{~ it.records :record:index }}
    <li>{{= index}}: {{= record.title }}</li>

To contrast, here is Jade’s indentation-based style:

if loggedIn
  a(href="/account") #{firstName} #{lastName}
  a(href="/login") Log In
  each record in records
    li #{index}: #{record.title}

Many DSLs are implemented in multiple languages (e.g. Jade and Haml). For instance, a PHP backend could share templates with Node backend. DSLs can be helpful for designers who work with templates because it doesn’t require them to learn a full-fledged language.

Some other notable libraries include Swig and Nunjucks.


Logic-less templates, a concept popularized by Mustache, essentially prevent any data massaging in the template itself. Although there are “logical” constructs provided (like if/then and iteration), any finessing of the data happens outside the template. Why? The goal is to separate concerns by preventing business logic from creeping into your views.

Let’s take a peak at Mustache:

  <a href="/account">{{firstName}} {{lastName}}</a>
  <a href="/login">Log In</a>

Other popular template engines in this vein include Handlebars and Dust; both add helpers to the base Mustache syntax. The Mustache parser, in particular, has been implemented for a lot of languages.

Programmatic templates

The last style we will explore is programmatic. Unlike the previous styles, which add custom syntax to HTML, these modules augment plain HTML and/or build it from scratch with data. For example, hyperglue processes plain HTML, like:


Then, by writing the following JavaScript code (using CSS selector syntax), it returns a populated HTML fragment:

var fragment = hyperglue(html, {
  a: {
    href: loggedIn ? "/account" : "/login",
    _text: loggedIn ? firstName + ' ' + lastName : "Login"
  'ul li': (record, index) {
      return { li: { _text: index + ': ' + record.title } }

To expound more on this concept, check out @substack’s article. Other programmatic examples include domjs and Plates.

Wrapping up

In this article, we looked at the types of template systems available for Node. In closing, here are some suggestions:

  1. If you are newer to Node template engines, start with something familiar to previous platforms you’ve used (in many cases this will be EJS). Then, branch out.
  2. Stuck in one style or one engine? Be brave, try another style. Learn its strengths and weaknesses.
  3. Need more help choosing a module for that next project? Garann Mean has setup a great site to help you.

Happy templating!

This article originally was published on the Strongloop blog.

What do you think? Submit a change/correction.

Up next:

Marc is the co-author of Node.js in Action and Node.js in Practice. He enjoys learning and writes technical stuff here and for IBM. Currently plays around with Go, TypeScript and Rust. Works as a full-stack engineer for @applieddataconsultants.