Pug 3.0.4 352,080 (MIT) replaces HTML syntax with indentation: no closing tags, no angle brackets, = for an escaped expression and #{} for interpolation inside text. Handlebars 4.7.9 131,260 (MIT), usually mounted through express-handlebars 9.0.1, keeps HTML and adds {{ }} for escaped output, {{{ }}} for raw, and blocks such as {{#each}}. It is deliberately logic-less: you cannot write an expression, only call a helper.
doctype html | <h1>{{title}}</h1>
html(lang="en") | <ul>
head | {{#each books}}
title= title | <li>{{title}} — {{year}}</li>
body | {{/each}}
h1= title | </ul>
ul |
each b in books | layouts/main.handlebars wraps it
li #{b.title} — #{b.year} | in <body>{{{body}}}</body>Against the same locals, Pug emits minified HTML with no whitespace between tags, while express-handlebars injects the template into the layout at {{{body}}} and preserves your formatting.
$ curl -s http://127.0.0.1:4188/pug (one line; wrapped here to fit the page) <!DOCTYPE html><html lang="en"><head><title>Bookshelf</title></head><body><h1>Bookshelf</h1> <ul><li>Dune — 1965</li><li>Neuromancer — 1984</li></ul></body></html> $ curl -s http://127.0.0.1:4188/hbs (first three lines) <!doctype html> <html lang="en"><head><meta charset="utf-8"><title>Bookshelf</title></head> <body><h1>Bookshelf</h1>
| Engine | Syntax | Layouts | Logic in template |
|---|---|---|---|
| EJS 6.0.1 689,492 | HTML + <% %> | Includes only | Any JavaScript |
| Pug 3.0.4 | Indentation | extends / block | Expressions, mixins |
| Handlebars 4.7.9 | HTML + {{ }} | express-handlebars | Registered helpers only |
Pick on the basis of who edits the templates. Pug's inheritance (extends layout plus named blocks) is the strongest layout system of the three, but its indentation rules are unforgiving and a designer pasting in HTML gets a parse error. Handlebars keeps files a designer can open and keeps business rules out of the view by force. All three escape by default and all three offer a raw form; the Handlebars triple-brace is the easiest to type by accident, so search for {{{ in review as you would for <%- %>.