Express Template Engines — Complete Guide to EJS, Pug, and Handlebars
In this tutorial, you will learn about Express Template Engines. We cover key concepts, practical examples, and best practices to help you master this topic.
Express template engines render dynamic HTML on the server by combining templates with data from route handlers, enabling server-side rendering of web pages.
What You'll Learn
By the end of this tutorial, you'll set up EJS, Pug, and Handlebars in Express, pass data to templates, use layouts and partials, and choose the right engine for your project.
Why Template Engines Matter
Server-side rendering (SSR) produces fully-rendered HTML on the server, improving SEO and initial page load speed. Template engines make SSR maintainable with reusable components.
Real-World Use
A dashboard application uses EJS to render user-specific data into HTML templates. The sidebar, header, and footer are partials reused across all pages, reducing code duplication.
Express Templates Learning Path
flowchart LR
A[Error Handling] --> B[Template Engines]
B --> C[Static Files]
C --> D[Sessions]
D --> E[Security]
A --> F{You Are Here}
style F fill:#f90,color:#fff
EJS Setup and Syntax
npm install ejs
app.set("view engine", "ejs");
app.set("views", "./views");
// views/index.ejs
// <h1>Welcome, <%= user.name %></h1>
// <% if (user.isAdmin) { %> <p>Admin Panel</p> <% } %>
app.get("/", (req, res) => {
res.render("index", { user: { name: "Alice", isAdmin: true } });
});
Pug Setup and Syntax
npm install pug
app.set("view engine", "pug");
app.set("views", "./views");
// views/index.pug
// h1= `Welcome, ${user.name}`
// if user.isAdmin
// p Admin Panel
app.get("/", (req, res) => {
res.render("index", { user: { name: "Alice", isAdmin: true } });
});
Handlebars Setup
npm install express-handlebars
import { engine } from "express-handlebars";
app.engine("handlebars", engine());
app.set("view engine", "handlebars");
app.set("views", "./views");
// views/index.handlebars
// <h1>Welcome, {{user.name}}</h1>
// {{#if user.isAdmin}}<p>Admin Panel</p>{{/if}}
Layouts and Partials
EJS
<!-- views/layout.ejs -->
<!DOCTYPE html><html><body>
<%- include('partials/header') %>
<%- body %>
<%- include('partials/footer') %>
</body></html>
Pug
//- views/layout.pug
html
body
include partials/header
block content
include partials/footer
Handlebars
{{! views/layouts/main.handlebars }}
<html><body>
{{> header}}
{{{body}}}
{{> footer}}
</body></html>
Passing Data to Templates
app.get("/users", async (req, res) => {
const users = await db.users.findAll();
res.render("users/index", {
title: "All Users",
users,
count: users.length,
isLoggedIn: true,
helpers: { formatDate: (d) => d.toLocaleDateString() }
});
});
Common Mistakes
1. Forgetting to Set views Directory
Express looks for views in the default directory. Always set app.set("views", "./views") explicitly.
2. Using Wrong Syntax for Escaping
EJS: <%= %> escapes HTML, <%- %> renders raw HTML. Using <%= %> for HTML strings escapes the tags.
3. Not Handling Empty Data
When data is undefined, templates throw errors. Always provide defaults or conditional checks.
4. Mixing Multiple Template Engines
Stick to one template engine per project. Mixing engines confuses developers and complicates the build Process.
5. Putting Too Much Logic in Views
Templates are for presentation. Keep business logic in controllers and pass computed values to views.
Practice Questions
1. What is the difference between server-side rendering and client-side rendering?
SSR renders HTML on the server and sends fully-rendered pages. CSR sends JavaScript that renders HTML in the browser.
2. How do you create a layout in EJS?
Use include statements. Create a layout.ejs with <%- include('header') %> and <%- include('footer') %> around the content.
3. Why use partials in templates?
Partials let you reuse HTML snippets (headers, footers, navigation) across multiple pages, reducing duplication.
4. What is the difference between <%= and <%- in EJS?
<%= escapes HTML (safe for user input). <%- outputs raw HTML (use only with trusted content).
5. Challenge: Create an EJS template that renders a list of users with a conditional admin badge.
<!-- views/users.ejs -->
<h1><%= title %></h1>
<ul>
<% users.forEach(user => { %>
<li>
<%= user.name %>
<% if (user.role === 'admin') { %>
<span class="badge">Admin</span>
<% } %>
</li>
<% }) %>
</ul>
FAQ
Mini Project: User Dashboard with EJS
Build a dashboard that renders user data with EJS templates.
// app.js
app.set("view engine", "ejs");
app.set("views", "./views");
app.get("/dashboard", (req, res) => {
res.render("dashboard", {
user: { name: "Alice", email: "alice@example.com", role: "admin" },
stats: { posts: 42, comments: 128, followers: 509 },
recentPosts: [
{ title: "Getting Started with Express", date: "2026-06-01" },
{ title: "Node.js Best Practices", date: "2026-06-15" }
]
});
});
<!-- views/dashboard.ejs -->
<h1>Welcome, <%= user.name %></h1>
<div class="stats">
<div>Posts: <%= stats.posts %></div>
<div>Comments: <%= stats.comments %></div>
<div>Followers: <%= stats.followers %></div>
</div>
<h2>Recent Posts</h2>
<ul>
<% recentPosts.forEach(post => { %>
<li><%= post.title %> (<%= post.date %>)</li>
<% }) %>
</ul>
What's Next
Express Static Files Express Sessions Express Security
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro