Express.js Using Templating Engines

In a standard static website, HTML files are delivered to the browser exactly as they are stored on the server. However, modern web applications need to display data that changes constantly—think of a user's profile name, a list of products in a shopping cart, or a news feed. This is where Templating Engines come in. They allow you to write HTML "blueprints" with placeholders that Express fills with real data before sending the final page to the user.

Developer Tip: Think of a template like a "Mail Merge" document. You write the structure once, and the engine swaps out variables like {username} for actual values from your database.

 

Key Features of Using Templating Engines

  • Dynamic Content Rendering: You can display data from databases, APIs, or user input directly inside your HTML structure.
  • Separation of Concerns: By keeping your HTML (the "View") separate from your JavaScript logic (the "Controller"), your codebase stays organized and much easier to debug.
  • Reusability: Most engines support "partials" or "includes," allowing you to define a single header or footer and reuse it across every page on your site.
  • Logic Support: You can use if statements to show content only to logged-in users or use loops to generate lists of items dynamically.

 

Setting Up a Templating Engine in Express.js

To get started, you need to choose an engine, install it via npm, and tell Express which one you are using. EJS (Embedded JavaScript) is one of the most popular choices because it uses standard HTML syntax with a few extra tags for logic.

Best Practice: While many engines exist, EJS is often the best choice for beginners because it doesn't require learning a new syntax—if you know HTML and JavaScript, you already know EJS.

Install the Templating Engine

Open your terminal in your project folder and run the following command to install EJS:

npm install ejs

Set Up Express to Use the Templating Engine

You don't need to "require" EJS in your code; Express handles that internally once you set the view engine property.

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

// Tell Express to use EJS as the rendering engine
app.set('view engine', 'ejs');

// Define where your template files are located
// By default, Express looks in a folder named 'views'
app.set('views', path.join(__dirname, 'views'));

app.get('/', (req, res) => {
    // res.render looks for 'index.ejs' in the 'views' folder
    res.render('index', { title: 'Home Page', message: 'Welcome to our site!' });
});

app.listen(3000, () => {
    console.log('Server is running on http://localhost:3000');
});
Watch Out: If you don't create a folder named views in your project root, Express will throw an error when you try to call res.render().

Create Template Files

Inside your views folder, create a file named index.ejs. Note the .ejs extension instead of .html.

Example of views/index.ejs:

<!DOCTYPE html>
<html>
<head>
    <title><%= title %></title>
</head>
<body>
    <h1><%= message %></h1>
    <p>The current year is <%= new Date().getFullYear() %></p>
</body>
</html>
Common Mistake: Forgetting the equals sign in <%= %>. Use <%= %> to output a value. If you use <% %> (without the =), the code executes but nothing is printed to the HTML.

 

Using Other Templating Engines

Depending on your project needs or personal style, you might prefer a different engine. Express is flexible and supports many options:

Pug (formerly Jade): Uses a shorthand, indentation-based syntax. It removes the need for brackets and closing tags, which results in very clean, minimal files.

npm install pug

Set up in Express:

app.set('view engine', 'pug');

Example views/index.pug (notice the lack of tags):


doctype html
html
  head
    title= title
  body
    h1 Welcome to #{title}!
    p This is rendered using Pug.

Handlebars (HBS): Focuses on keeping logic strictly separate from the view. It uses "Mustache" style braces {{ }} and is very popular in the JavaScript community.

npm install hbs

Set up in Express:

app.set('view engine', 'hbs');

Example views/index.hbs:

<html>
  <head><title>{{title}}</title></head>
  <body>
    <h1>Welcome to {{title}}!</h1>
  </body>
</html>

 

Passing Data to Templates

In a real-world scenario, you will often pass arrays of objects (like a list of blog posts or users) to your templates. You can iterate through this data directly in the view.

Example Route:

app.get('/team', (req, res) => {
    const members = [
        { name: 'Alice', role: 'Developer' },
        { name: 'Bob', role: 'Designer' },
        { name: 'Charlie', role: 'Manager' }
    ];
    res.render('team', { members });
});

Template (views/team.ejs):

<h1>Meet the Team</h1>
<ul>
    <% members.forEach(function(member) { %>
        <li><strong><%= member.name %></strong> - <%= member.role %></li>
    <% }); %>
</ul>
Developer Tip: When passing data, keep the object keys descriptive. Instead of passing { d: data }, use { users: data } so your template is easier to read for other developers.

 

Using Static Files with Templates

Templates only handle the structure and data. To make your pages look good, you need CSS, and for interactivity, you need client-side JavaScript. Express uses a built-in middleware called express.static to serve these assets.

Example:

// Serves everything inside the 'public' folder
app.use(express.static('public'));

Assuming you have a file at public/css/style.css, you can link it in your EJS template like this:

<!-- Note: You don't include 'public' in the path -->
<link rel="stylesheet" href="/css/style.css">
Best Practice: Always place your app.use(express.static(...)) call near the top of your middleware stack to ensure your assets are available for every route.

 

Summary

Templating engines are essential for building interactive, data-driven web applications with Express.js. By choosing an engine like EJS, Pug, or Handlebars, you can transform static HTML into dynamic views that respond to user data and application logic. Remember to keep your logic in your routes and your presentation in your templates to ensure your code stays clean, modular, and easy to maintain as your project grows.