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:
- Install the template engine
- Configure Express to use the engine
- 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:
npm install ejs
Step 2: Configure Express
Next, configure Express to use EJS as the template engine:
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
:
<!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:
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.
Popular Template Engines for Express
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:
// Install
npm install ejs
// Setup
app.set('view engine', 'ejs');
Sample EJS Template:
<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:
// Install
npm install pug
// Setup
app.set('view engine', 'pug');
Sample Pug Template:
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:
// 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:
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)
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)
<!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)
<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)
<%- 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:
-
Caching: Most template engines cache compiled templates in production, which improves performance significantly.
-
Compilation vs. Interpretation: Some engines compile templates to JavaScript functions (faster), while others interpret them at runtime (slower).
-
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
-
Keep templates clean: Avoid complex logic in templates - move it to your routes or controllers.
-
Use layouts and partials: Don't repeat common elements like headers and footers.
-
Pass only necessary data: Avoid passing large objects to templates when not needed.
-
Cache templates in production: Most engines have built-in caching, but make sure it's enabled.
-
Handle errors gracefully: Use try/catch blocks when rendering templates and provide user-friendly error pages.
-
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
- Express.js documentation on template engines
- EJS documentation
- Pug documentation
- Handlebars documentation
Practice Exercises
- Create a simple Express application that displays a list of products using EJS templates.
- Modify your application to include partials for header and footer sections.
- Add a new page that displays detailed information about a selected product.
- Try implementing the same application using a different template engine (Pug or Handlebars) and compare the differences.
- 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! :)