Express.js Template Engines

Template engines let you build dynamic HTML pages on the server. Instead of writing every HTML page as a fixed file, you create a template with placeholders. Express fills those placeholders with real data before sending the page to the browser. This is how blogs display different articles, e-commerce sites show different product details, and dashboards display personalized user data.

Static HTML vs Dynamic Templates

Think of a static HTML file like a printed flyer — everyone receives the same content. A template is like a custom printed letter: the layout is the same, but the name and address change for each recipient.

Static HTML file:             Template with data:
─────────────────            ──────────────────────────────
<h1>Hello, User!</h1>       <h1>Hello, {{ name }}!</h1>
                                         ↓ filled with data
                              <h1>Hello, Alice!</h1>
                              (or Bob, or anyone else)

Popular Template Engines for Express

┌─────────────────────────────────────────────────────────────┐
│            Template Engines for Express.js                  │
├──────────────┬─────────────────────┬────────────────────────┤
│ Engine       │ File Extension      │ Syntax Style           │
├──────────────┼─────────────────────┼────────────────────────┤
│ EJS          │ .ejs                │ <% JavaScript %>       │
│ Pug (Jade)   │ .pug                │ Indentation-based      │
│ Handlebars   │ .hbs                │ {{ variable }}         │
│ Nunjucks     │ .njk                │ Jinja2-like            │
└──────────────┴─────────────────────┴────────────────────────┘

This topic focuses on EJS (Embedded JavaScript) because its syntax looks close to regular HTML, making it the easiest engine for beginners to learn.

Install EJS

npm install ejs

Configure Express to Use EJS

Tell Express which engine to use and where your templates live:

const express = require('express');
const app = express();

app.set('view engine', 'ejs');
app.set('views', './views');  // Folder where templates live

app.listen(3000);

Create a folder named views in your project root. Place all your EJS template files inside it.

Project Structure with Templates

my-express-app/
│
├── views/                      ← Template files
│   ├── index.ejs
│   ├── about.ejs
│   └── product.ejs
│
├── public/                     ← Static files (CSS, images)
│   └── css/
│       └── style.css
│
└── app.js

Rendering a Template with res.render()

Use res.render() to send a template as the response. Pass the template name (without extension) and a data object:

app.get('/', (req, res) => {
  res.render('index', {
    title: 'Home Page',
    username: 'Alice',
    isLoggedIn: true
  });
});

Express finds views/index.ejs, fills in the data, converts it to HTML, and sends it to the browser.

EJS Syntax Basics

EJS uses special tags embedded inside regular HTML:

┌────────────────────────────────────────────────────────────┐
│                     EJS Tag Types                          │
├──────────────┬─────────────────────────────────────────────┤
│ <%= value %> │ Output — prints the value to the page       │
│ <% code %>   │ Execute — runs JS but prints nothing        │
│ <%- html %>  │ Unescaped — outputs raw HTML (use carefully)│
│ <%# comment%>│ Comment — not sent to the browser           │
└──────────────┴─────────────────────────────────────────────┘

Example: views/index.ejs

<!DOCTYPE html>
<html>
<head>
  <title><%= title %></title>
</head>
<body>
  <h1>Welcome, <%= username %>!</h1>

  <% if (isLoggedIn) { %>
    <p>You are logged in.</p>
  <% } else { %>
    <p>Please log in.</p>
  <% } %>
</body>
</html>

When Express renders this template with { title: 'Home Page', username: 'Alice', isLoggedIn: true }, the browser receives this HTML:

<!DOCTYPE html>
<html>
<head>
  <title>Home Page</title>
</head>
<body>
  <h1>Welcome, Alice!</h1>
  <p>You are logged in.</p>
</body>
</html>

Looping Through Arrays in EJS

Render lists of items by looping over an array inside the template:

// app.js
app.get('/products', (req, res) => {
  const products = [
    { id: 1, name: 'Laptop', price: 999 },
    { id: 2, name: 'Mouse', price: 29 },
    { id: 3, name: 'Keyboard', price: 79 }
  ];
  res.render('products', { products });
});
<!-- views/products.ejs -->
<h2>Product List</h2>
<ul>
  <% products.forEach(product => { %>
    <li>
      <%= product.name %> — $<%= product.price %>
    </li>
  <% }) %>
</ul>

EJS executes the forEach loop and outputs one <li> element for each product.

Partials: Reusable Template Pieces

A partial is a template fragment that you include inside other templates. Use partials for repeated sections like headers and footers, so you only write them once:

views/
├── partials/
│   ├── header.ejs
│   └── footer.ejs
├── index.ejs
└── about.ejs
<!-- views/partials/header.ejs -->
<nav>
  <a href="/">Home</a>
  <a href="/about">About</a>
</nav>
<!-- views/index.ejs -->
<%- include('partials/header') %>
<h1>Welcome to the Home Page</h1>
<%- include('partials/footer') %>

The <%- tag (with a dash) outputs unescaped HTML, which is correct here because you are inserting raw HTML from your own trusted partials.

Rendering How the Request-to-Response Flow Works with Templates

Browser: GET /profile
           │
           ▼
  Express route handler runs:
  ┌─────────────────────────────────────────────┐
  │ app.get('/profile', (req, res) => {         │
  │   res.render('profile', {                   │
  │     name: 'Alice',                          │
  │     email: 'alice@example.com'              │
  │   });                                       │
  │ });                                         │
  └────────────────────┬────────────────────────┘
                       │
                       ▼
           EJS engine reads views/profile.ejs
           Inserts name and email values
           Produces complete HTML string
                       │
                       ▼
           Express sends HTML to browser
           Browser displays the page

Passing Data from Database to Template

In real applications, data comes from a database. The route fetches data and passes it to the template:

app.get('/blog/:id', async (req, res) => {
  try {
    const post = await BlogPost.findById(req.params.id);
    res.render('blog-post', {
      title: post.title,
      author: post.author,
      content: post.content,
      date: post.createdAt
    });
  } catch (err) {
    res.status(404).render('not-found', { message: 'Post not found' });
  }
});

Summary

Template engines let Express generate dynamic HTML by merging data with reusable templates. Install EJS with npm install ejs and register it with app.set('view engine', 'ejs'). Store template files in the views folder and render them using res.render('templateName', dataObject). Use EJS tags to output variables, run JavaScript logic, and loop through arrays. Build partials for repeated layout sections like headers and footers, and include them with <%- include('partials/header') %>. Fetch database data in your route handler and pass it to the template for display.

Leave a Comment

Your email address will not be published. Required fields are marked *