Skip to main content

Express Template Engines

Introduction

When building web applications with Express.js, you'll often need to generate HTML dynamically based on data from your server. While you could manually concatenate strings to create HTML, this approach quickly becomes cumbersome and error-prone. This is where template engines come to the rescue!

Template engines allow you to work with template files that contain variables and simple logic. These templates are then processed by Express to generate dynamic HTML sent to the client. They provide a cleaner way to separate your application's logic from its presentation layer.

In this guide, we'll explore what template engines are, how they work with Express, and examine some popular options.

What Are Template Engines?

A template engine enables you to use static template files in your application. At runtime, the template engine replaces variables in these files with actual values and transforms the template into an HTML file sent to the client. This approach makes it easier to design HTML pages with dynamic content.

Some key benefits of using template engines include:

  • Separation of concerns: Keep your presentation logic separate from business logic
  • Cleaner code: Avoid messy string concatenation when generating HTML
  • Reusable components: Create layouts and partials to avoid duplicating code
  • Powerful features: Many template engines provide features like loops, conditionals, and filters

Setting Up a Template Engine in Express

Setting up a template engine in Express involves three main steps:

  1. Install the template engine
  2. Configure Express to use the engine
  3. Create template files and render them

Let's see how this works with a simple example using EJS (Embedded JavaScript), one of the most popular template engines for Express.

Step 1: Install the Template Engine

First, let's install EJS using npm:

bash
npm install ejs

Step 2: Configure Express

Next, configure Express to use EJS as the template engine:

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

// Set EJS as the view engine
app.set('view engine', 'ejs');

// Specify the directory where your template files will be located
app.set('views', './views');

Step 3: Create and Render Templates

Create a directory called views in your project root and add an EJS template file. For example, create views/index.ejs:

html
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h1><%= heading %></h1>
<p>Welcome to <%= title %></p>

<h2>User Profile:</h2>
<% if (user) { %>
<p>Name: <%= user.name %></p>
<p>Email: <%= user.email %></p>
<% } else { %>
<p>No user logged in</p>
<% } %>

<h2>Product List:</h2>
<ul>
<% products.forEach(function(product) { %>
<li><%= product.name %> - $<%= product.price %></li>
<% }); %>
</ul>
</body>
</html>

Now, render this template from your Express route:

javascript
app.get('/', (req, res) => {
// Data to pass to the template
const data = {
title: 'My Express App',
heading: 'Hello from EJS!',
user: {
name: 'John Doe',
email: '[email protected]'
},
products: [
{ name: 'Laptop', price: 999 },
{ name: 'Phone', price: 699 },
{ name: 'Tablet', price: 399 }
]
};

// Render the 'index' template with data
res.render('index', data);
});

When a user visits the homepage, Express will process the EJS template, insert the data, and return the resulting HTML to the browser.

Express is flexible and works with many template engines. Here are three popular options:

1. EJS (Embedded JavaScript)

EJS uses a syntax that's familiar to developers who know HTML and JavaScript:

javascript
// Install
npm install ejs

// Setup
app.set('view engine', 'ejs');

Sample EJS Template:

html
<h1><%= title %></h1>
<ul>
<% users.forEach(function(user) { %>
<li><%= user.name %></li>
<% }); %>
</ul>

Pros:

  • Familiar syntax (HTML with JavaScript)
  • Minimal learning curve
  • Good performance

Cons:

  • Templates can become messy with complex logic
  • No built-in layout system (though you can use EJS layouts package)

2. Pug (formerly Jade)

Pug uses whitespace-significant syntax and less code compared to HTML:

javascript
// Install
npm install pug

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

Sample Pug Template:

pug
h1= title
ul
each user in users
li= user.name

Pros:

  • Clean, concise syntax
  • Built-in layout inheritance
  • Powerful features like mixins

Cons:

  • Syntax is quite different from HTML (steeper learning curve)
  • Sensitive to whitespace and indentation

3. Handlebars (via express-handlebars)

Handlebars uses a logic-less templating approach with mustache-like syntax:

javascript
// Install
npm install express-handlebars

// Setup
const exphbs = require('express-handlebars');
app.engine('handlebars', exphbs.engine({ defaultLayout: 'main' }));
app.set('view engine', 'handlebars');

Sample Handlebars Template:

handlebars
<h1>{{title}}</h1>
<ul>
{{#each users}}
<li>{{this.name}}</li>
{{/each}}
</ul>

Pros:

  • Logic-less templates keep presentation separate from logic
  • Built-in layout and partials system
  • Widely used across different platforms

Cons:

  • Limited logic capabilities in templates by design
  • May require more helper functions for complex operations

Real-World Application: Building a Blog

Let's see a more complete example of using template engines to build a simple blog with EJS:

Project Structure

/blog-app
/node_modules
/public
/css
style.css
/views
/partials
header.ejs
footer.ejs
/layouts
main.ejs
index.ejs
post.ejs
about.ejs
app.js
package.json

App Setup (app.js)

javascript
const express = require('express');
const path = require('path');
const app = express();
const port = 3000;

// Set up EJS as view engine
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));

// Serve static files
app.use(express.static(path.join(__dirname, 'public')));

// Sample blog data
const blogPosts = [
{
id: 1,
title: "Getting Started with Express",
excerpt: "Learn the basics of Express.js...",
content: "Express.js is a minimal and flexible Node.js web application framework...",
author: "Jane Smith",
date: "2023-03-15"
},
{
id: 2,
title: "Template Engines in Express",
excerpt: "Exploring different template engines...",
content: "Template engines allow you to generate HTML dynamically...",
author: "John Doe",
date: "2023-03-20"
}
];

// Routes
app.get('/', (req, res) => {
res.render('index', {
title: 'Express Blog',
posts: blogPosts
});
});

app.get('/post/:id', (req, res) => {
const post = blogPosts.find(p => p.id === parseInt(req.params.id));
if (!post) return res.status(404).send('Post not found');

res.render('post', {
title: post.title,
post
});
});

app.get('/about', (req, res) => {
res.render('about', { title: 'About Us' });
});

app.listen(port, () => {
console.log(`Blog app listening at http://localhost:${port}`);
});

Main Layout (views/layouts/main.ejs)

html
<!DOCTYPE html>
<html>
<head>
<title><%= title %> | Express Blog</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<%- include('../partials/header') %>

<main>
<%- body %>
</main>

<%- include('../partials/footer') %>
</body>
</html>

Header Partial (views/partials/header.ejs)

html
<header>
<h1>Express Blog</h1>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>

Blog Home Page (views/index.ejs)

html
<%- include('layouts/main', { body: `
<h1>Latest Blog Posts</h1>

<div class="post-list">
<% if (posts.length > 0) { %>
<% posts.forEach(post => { %>
<article class="post-card">
<h2><a href="/post/<%= post.id %>"><%= post.title %></a></h2>
<p class="meta">By <%= post.author %> on <%= post.date %></p>
<p><%= post.excerpt %></p>
<a href="/post/<%= post.id %>" class="read-more">Read more</a>
</article>
<% }); %>
<% } else { %>
<p>No posts found.</p>
<% } %>
</div>
` }) %>

This real-world example shows how template engines help organize your code by separating the layout, partials, and page content.

Template Engine Performance Considerations

When choosing a template engine, you might want to consider performance:

  1. Caching: Most template engines cache compiled templates in production, which improves performance significantly.

  2. Compilation vs. Interpretation: Some engines compile templates to JavaScript functions (faster), while others interpret them at runtime (slower).

  3. Complexity: More complex template features may impact performance.

For most applications, the performance differences between popular template engines won't significantly impact your application, so choose based on ease of use and team familiarity.

Best Practices for Using Template Engines

  1. Keep templates clean: Avoid complex logic in templates - move it to your routes or controllers.

  2. Use layouts and partials: Don't repeat common elements like headers and footers.

  3. Pass only necessary data: Avoid passing large objects to templates when not needed.

  4. Cache templates in production: Most engines have built-in caching, but make sure it's enabled.

  5. Handle errors gracefully: Use try/catch blocks when rendering templates and provide user-friendly error pages.

  6. Use appropriate escaping: Most template engines escape output by default to prevent XSS attacks, but be aware of how to output unescaped content when needed.

Summary

Template engines are essential tools for building dynamic web applications with Express. They provide a clean way to separate your HTML presentation from your application logic, making your code more maintainable and easier to understand.

In this guide, we explored:

  • What template engines are and how they work with Express
  • Setting up and configuring template engines
  • Popular template engines like EJS, Pug, and Handlebars
  • A real-world example of building a blog with templates
  • Performance considerations and best practices

Choose a template engine that fits your team's preferences and project requirements. For beginners, EJS is often recommended due to its familiar syntax, but each engine has its strengths and ideal use cases.

Additional Resources

Practice Exercises

  1. Create a simple Express application that displays a list of products using EJS templates.
  2. Modify your application to include partials for header and footer sections.
  3. Add a new page that displays detailed information about a selected product.
  4. Try implementing the same application using a different template engine (Pug or Handlebars) and compare the differences.
  5. Create a template that includes conditionals to display different content for authenticated and unauthenticated users.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)