Handlebars is a logic-minimal templating engine using mustache-style {{ }} placeholders, intentionally restricting what you can do inside a template to keep view logic out of the presentation layer. This lesson covers setting it up with express-handlebars.
Handlebars Syntax Basics
Handlebars uses double curly braces, {{ }}, for escaped output, and triple braces, {{{ }}}, for raw, unescaped HTML. Unlike EJS or Pug, Handlebars deliberately does not allow arbitrary JavaScript expressions inside templates, only variable lookups and a small set of built-in and custom "helpers".
Express itself doesn't ship a Handlebars integration, express-handlebars is the standard package that wires Handlebars into Express's res.render() and view engine system.
app.engine('handlebars', engine()) registers the Handlebars rendering function for the .handlebars file extension.
Handlebars Syntax Reference
{{ value }} escaped output
{{{ html }}} raw, unescaped output
{{#if condition}} ... {{/if}}
{{#each items}} ... {{/each}}
{{> partialName}} render a partial
Handlebars is intentionally "logic-less", complex logic belongs in helpers or your route handler, not the template.
Built-in block helpers cover the essentials: if, unless, each, with.
Custom helpers extend Handlebars with your own reusable template functions.
{{> partialName}} renders a registered partial, similar to includes in other engines.
Handlebars Cheatsheet
Common Handlebars syntax for output, control flow, and partials.
Task
Syntax
Escaped output
{{ user.name }}
Raw HTML output
{{{ trustedHtml }}}
Conditional
{{#if loggedIn}} ... {{else}} ... {{/if}}
Loop
{{#each products}} {{this.name}} {{/each}}
Partial
{{> header}}
Layout
layoutsDir + defaultLayout option in engine()
Writing Custom Helpers
When a template needs logic beyond the built-in helpers, formatting a date, pluralizing a word, define a custom helper function and register it when configuring the engine.
express-handlebars includes built-in layout support, a shared wrapper template rendered around every page's content, configured once instead of repeated per template.
Trying to write arbitrary JavaScript expressions directly in a template, Handlebars deliberately doesn't support this.
Using {{{ }}} for user-supplied content, bypassing escaping and risking XSS.
Forgetting to register app.engine('handlebars', engine()) before setting the view engine.
Duplicating the same page wrapper markup in every template instead of using layouts.
Key Takeaways
Handlebars uses mustache-style {{ }} placeholders and is intentionally logic-minimal.
express-handlebars wires Handlebars into Express's view engine system.
Built-in helpers (if, each, with) and custom helpers cover most templating needs.
Built-in layout support avoids duplicating shared page structure across templates.
Pro Tip
When you find yourself wanting to write real JavaScript logic inside a Handlebars template, that's a signal to move the logic into a custom helper or, better, prepare the final data shape in your route handler before rendering.
You now know three major Express template engines. Next, learn how to test your Express applications in the Express Testing lesson.