Skip to content

Igo Dust

Introduction

Dust.js was a great templating engine, maintained and then abandoned by LinkedIn. After looking for an equivalent that was lightweight and performant, we ended up rewriting it from scratch — with zero dependencies and modern async/await patterns.

Key Features

  • Fully Async: Built with async/await, all rendering returns Promises
  • High Performance: Optimized compilation and rendering engine
  • HTML-safe by default: All output is HTML-encoded unless you opt out
  • Zero Dependencies: Lightweight and fast
  • Express Integration: Works as an Express view engine

Benchmark

We benchmark Igo Dust against other templating engines (Mustache, Handlebars, EJS, Pug, …) — see arnaudm/templating-engine-bench for the latest numbers.

Tooling

The VSCode extension for Igo Dust is available on the marketplace (also vendored as the syntax grammar in this site).

Differences from Dust.js

Igo Dust is not a drop-in replacement for Dust.js. It shares a similar syntax but has important differences:

1. Dot notation required in loops and sections

Inside a loop or section, you must use . to access properties of the current item:

js
// Template
{#users}{.name}{/users}

Without the dot, {name} looks up the root context, not the current item. This is the biggest difference from Dust.js.

You can also rename the iterator with it=:

js
{#users it="user"}{user.name}{/users}

2. No hierarchical scope chain

Dust.js searches up a scope chain to resolve variables. Igo Dust uses a flat context with explicit save/restore. Inside a section, only .property accesses the current context — there is no automatic fallback to parent contexts.

3. Empty arrays are falsy

In Igo Dust, [] evaluates to false in conditionals:

js
{?items}Has items{:else}No items{/items}
// With items = [] → "No items"

4. Async rendering only

All rendering methods return Promises. There is no callback-based API.

Quick Start

js
const dust = require('@igojs/dust');

const result = await dust.render('Hello, {name}!', { name: 'World' });
// => Hello, World!

Installation

bash
npm install @igojs/dust

Using with Express

js
const express = require('express');
const dust = require('@igojs/dust');
const app = express();

dust.configure({
  views: './views',
  cache: app.get('env') === 'production',
});

app.engine('dust', dust.engine);
app.set('view engine', 'dust');
app.set('views', './views');

app.get('/', (req, res) => {
  res.render('welcome/index', { name: 'World' });
});

app.listen(3000);

Template (views/welcome/index.dust):

html
<!DOCTYPE html>
<html>
<head><title>{title}</title></head>
<body>
  <h1>Hello, {name}!</h1>
</body>
</html>

Using the API

js
const dust = require('@igojs/dust');

// Render from string
const html = await dust.render('<h1>Hello, {name}!</h1>', { name: 'World' });

// Render from file
dust.configure({ views: './templates' });
const html = await dust.renderFile('template.dust', { name: 'World' });

Configuration

js
dust.configure({
  views:      './views',   // Template directory
  cache:      true,        // Cache compiled templates (recommended in production)
  htmlencode: true,        // HTML-encode all output (default: true)
  htmltrim:   true,        // Trim whitespace (default: true)
});
OptionTypeDefaultDescription
viewsstring'./views'Directory containing template files
cachebooleanfalseCache compiled templates
htmlencodebooleantrueHTML-encode all output (prevents XSS)
htmltrimbooleantrueRemove line breaks and trim whitespace

Next Steps

  • Basics — Syntax fundamentals
  • Loops — Iterating over arrays and objects
  • Logic — Conditional rendering
  • Helpers — Built-in and custom helpers
  • Filters — Output transformation
  • Partials — Template reuse and layouts
  • API — Full API reference

Released under the ISC license.