ExpressJS - Templating
Express.js - Modern Templating
Section titled “Express.js - Modern Templating”When building web applications, you often need to generate dynamic HTML pages. Instead of manually constructing HTML strings in your server code (which is error-prone and hard to maintain), you can use a templating engine. A templating engine allows you to write static template files with special syntax to dynamically insert data on the server before sending the page to the client.
Pug (formerly known as Jade) is a popular and powerful templating engine for Node.js and browsers. It features a clean, indentation-based syntax that simplifies HTML, and it supports features like includes, inheritance, and interpolation. While we’ll focus on Pug, other popular engines like EJS (uses plain HTML with <% %> tags) and Handlebars (uses {{ }} tags) are also excellent choices.
Setting Up Pug with Express
Section titled “Setting Up Pug with Express”First, you need to add Pug as a project dependency. Open your terminal in the project root and run:
npm install pugNext, configure Express to use Pug as its view engine. You don’t need to require('pug') explicitly; Express handles it internally when you set the engine. Add these two lines to your main application file (e.g., index.js):
// Set the view engine to Pugapp.set('view engine', 'pug');
// Specify the directory where the template files are locatedapp.set('views', './views');By convention, Express looks for templates in a views directory in the project root. Let’s create this directory and a simple Pug file inside it.
Your First Pug Template
Section titled “Your First Pug Template”Create a new directory named views. Inside it, create a file named first_view.pug with the following content:
doctype htmlhtml(lang="en") head meta(charset="UTF-8") meta(name="viewport", content="width=device-width, initial-scale=1.0") title Hello Pug body p.greetings#people Hello World!Note on Modern Practices:
- The
lang="en"attribute on the<html>tag is good for accessibility and SEO. - The
viewportmeta tag is essential for responsive design on mobile devices.
Now, create a route in index.js to render this template using res.render():
app.get('/first-template', (req, res) => { // Express will look for 'first_view.pug' in the 'views' directory res.render('first_view');});Your complete index.js should now look something like this:
index.js
Section titled “index.js”import express from 'express';
const app = express();const PORT = process.env.PORT || 3000;
// Configure the view engineapp.set('view engine', 'pug');app.set('views', './views');
// Route to render the templateapp.get('/first-template', (req, res) => { res.render('first_view');});
app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`);});Run your server (npm run dev if you’ve set up a script with nodemon) and navigate to http://localhost:3000/first-template. You will see a page with the text “Hello World!”. Pug compiles your clean .pug file into standard HTML:
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Hello Pug</title> </head> <body> <p class="greetings" id="people">Hello World!</p> </body></html>Core Pug Features
Section titled “Core Pug Features”Passing Data to Templates
Section titled “Passing Data to Templates”The real power of templating comes from passing dynamic data from your route handler to the template. The second argument of res.render() is an object containing the data you want to pass.
Let’s create a new route and a corresponding template:
Route Handler (in index.js)
Section titled “Route Handler (in index.js)”app.get('/profile/:username', (req, res) => { const user = { name: req.params.username, isLoggedIn: true, lastLogin: new Date(), interests: ['Web Development', 'Node.js', 'Express'] };
res.render('profile', { title: `${user.name}'s Profile`, user: user });});Template (views/profile.pug)
Section titled “Template (views/profile.pug)”doctype htmlhtml(lang="en") head title= title body //- This is a Pug comment. It will not appear in the HTML output.
// Use '=' to output escaped HTML. This prevents XSS attacks. h1 Welcome, #{user.name}!
// Use 'if/else' for conditionals if user.isLoggedIn p Your last login was on #{user.lastLogin.toLocaleDateString()}. else p Please log in to see your details.
// Use 'each' for loops if user.interests.length h2 Your Interests: ul each interest in user.interests li= interest else p You have no listed interests.Now, if you visit http://localhost:3000/profile/Alice, Pug will use the user object to generate a dynamic HTML page. This technique of embedding variables directly into text is called interpolation (#{variableName}).
Conditionals and Loops
Section titled “Conditionals and Loops”As seen above, you can use JavaScript-like control flow directly in your Pug templates:
- Conditionals:
if,else if,else, andunless(which isif !(...)). - Iteration: Use
each val in arrayoreach val, index in arrayto loop over arrays. You can also usewhileloops.
Includes and Layouts (DRY Principle)
Section titled “Includes and Layouts (DRY Principle)”To avoid repeating code like headers and footers on every page (following the Don’t Repeat Yourself principle), Pug offers include and template inheritance (extends and block).
Using include: Create reusable components.
views/partials/header.pug
Section titled “views/partials/header.pug”header nav a(href='/') Home a(href='/about') Aboutviews/partials/footer.pug
Section titled “views/partials/footer.pug”footer p © #{new Date().getFullYear()} My Awesome Appviews/home.pug
Section titled “views/home.pug”doctype htmlhtml(lang="en") head title Home Page body include partials/header.pug
main h1 Welcome to the Home Page!
include partials/footer.pugUsing Layouts: A more powerful method is to define a base layout and extend it.
views/layout.pug (Base Template)
Section titled “views/layout.pug (Base Template)”doctype htmlhtml(lang="en") head title= title || 'My App' link(rel='stylesheet', href='/css/style.css') body include partials/header.pug
main.container block content //- The content of child templates will be inserted here
include partials/footer.pugviews/about.pug (Child Template)
Section titled “views/about.pug (Child Template)”extends layout.pug
block content h1 About Us p This is the about page for our awesome application.When to Use Server-Side Rendering (SSR)
Section titled “When to Use Server-Side Rendering (SSR)”Using a template engine like Pug is a form of Server-Side Rendering (SSR). This approach is excellent for:
- Content-heavy websites: Blogs, e-commerce sites, and news portals benefit from fast initial load times and strong SEO.
- Search Engine Optimization (SEO): Search engines can easily crawl and index pre-rendered HTML content.
- Simplicity: For many applications, SSR is simpler to set up and manage than a full client-side framework.
The alternative is to use Express to build a REST API that sends JSON data to a separate Single-Page Application (SPA) built with a framework like React, Vue, or Angular. The SPA then handles all the rendering in the user’s browser. This is a great choice for highly interactive, application-like experiences (e.g., dashboards, social media feeds).