Express.js and The Pug Template

The view layer is the part of the MVC architecture that the user can see, so of course, it should be created with HTML.

javascript
1app.get("/", (req, res) => {
2  res.send(`<!DOCTYPE html>
3<html>
4<head>
5    <meta charset="UTF-8" />
6    <title>title</title>
7</head>
8<body>
9    . . .
10</body>
11</html>`);
12});

But, there is a problem, because in practice, we cannot hard code the entire webpage.

For example, what if we need to embed variables inside the HTML template? Or, what if we need to conditionally render a piece of HTML based on certain requirements? Essentially, we need to add some programming features to the static HTML page.

This is why we need a template engine. Think of it as a mini-framework that works inside Express. Its job is to generate a proper HTML page for the view layer based on a template.

Getting started with Pug

The default template engine for Express is Pug, which can be installed with the following command:

bash
1npm install pug --save

Create an index.pug file inside the views directory.

text
1.
2├── controllers
3├── database.sqlite
4├── index.js
5├── models
6│   └── user.js
7├── package-lock.json
8├── package.json
9├── routes
10└── views
11    └── index.pug  <===

views/index.pug

pug
1doctype html
2html
3  head
4    title Page Title
5  body
6    p Hello, World!

This will be the template for our homepage, which will be rendered when you visit the root route (/).

To use this template, you must inform Express where the template files are located, as well as what template engine is used.

index.js

javascript
1app.set("views", "./views"); // Defines the location of the view templates
2app.set("view engine", "pug"); // Defines the template engine
3
4app.get("/", (req, res) => {
5  res.render("index");
6});

Also notice that instead of sending the response using send(), you must use the render() method here. Because Express needs to render the .pug template into an actual HTML page first, and then the rendered page will be transferred to the client.

Open Thunder Client and send a GET request to /. You will see the rendered HTML being returned.

Basic syntax

Now, let's take a closer look at our example index.pug.

pug
1doctype html
2html
3  head
4    title Page Title
5  body
6    p Hello, World!

It should look familiar to you, because they are just HTML tags, except, instead of a pair of start and end tags, Pug uses indentations. This example will render into:

html
1<!doctype html>
2<html>
3  <head>
4    <title>Page Title</title>
5  </head>
6  <body>
7    <p>Hello, World!</p>
8  </body>
9</html>

To include attributes, use a pair of parentheses.

pug
1doctype html
2html
3  head
4    title Page Title
5  body
6    p(class='red bold' id='paragraph') Hello, World!
html
1<!doctype html>
2<html>
3  <head>
4    <title>Page Title</title>
5  </head>
6  <body>
7    <p class="red bold" id="paragraph">Hello, World!</p>
8  </body>
9</html>

Data interpolation

Data interpolation means that you can dynamically render a webpage by passing data from Express to Pug. For example,

index.js

javascript
1app.get("/", (req, res) => {
2  let name = "John";
3  res.render("index", {
4    name,
5  });
6});

The variable name will be passed from Express to Pug (or any other template engines you want), in the form of an object. The variable can then be accessed inside Pug using #{name}.

views/index.pug

pug
1doctype html
2html
3  head
4    title Page Title
5  body
6    p Hello, #{name}!

And the following HTML page will be rendered:

html
1<!doctype html>
2<html>
3  <head>
4    <title>Page Title</title>
5  </head>
6  <body>
7    <p>Hello, John!</p>
8  </body>
9</html>

Conditional rendering

You can add flow control logics in the Pug template, which allows you to render a piece of HTML code only when certain conditions are satisfied.

index.js

javascript
1app.get("/", (req, res) => {
2  let authorized = true;
3  res.render("index", {
4    authorized,
5  });
6});

views/index.pug

pug
1doctype html
2html
3  head
4    title Page Title
5  body
6    if authorized
7      p You are authorized to access this page.
8    else
9      p You are NOT authorized to access this page.
html
1<!doctype html>
2<html>
3  <head>
4    <title>Page Title</title>
5  </head>
6  <body>
7    <p>You are authorized to access this page.</p>
8  </body>
9</html>

Iterations

Loops can also be used inside the Pug template. This can be useful when you need to repeat the same piece of HTML code with only slight differences, such as a list of users, a list of articles, and so on.

Iterate over array

index.js

javascript
1app.get("/", (req, res) => {
2  let arr = ["One", "Two", "Three"];
3  res.render("index", {
4    arr,
5  });
6});

views/index.pug

pug
1doctype html
2html
3  head
4    title Page Title
5  body
6    ul
7      each value in arr
8        li= value

Note that the equal sign (=) cannot be left out here. It tells Pug that value is a variable and should not be printed as a string.

html
1<!doctype html>
2<html>
3  <head>
4    <title>Page Title</title>
5  </head>
6  <body>
7    <ul>
8      <li>One</li>
9      <li>Two</li>
10      <li>Three</li>
11    </ul>
12  </body>
13</html>

Iterate over object

index.js

javascript
1app.get("/", (req, res) => {
2  let obj = { one: "One", two: "Two", three: "Three" };
3  res.render("index", {
4    obj,
5  });
6});

views/index.pug

pug
1doctype html
2html
3  head
4    title Page Title
5  body
6    ul
7      each value, key in obj
8        li= key + ": " + value
html
1<!doctype html>
2<html>
3  <head>
4    <title>Page Title</title>
5  </head>
6  <body>
7    <ul>
8      <li>one: One</li>
9      <li>two: Two</li>
10      <li>three: Three</li>
11    </ul>
12  </body>
13</html>

each else

Lastly, you can define a special condition by adding an else block, which will be rendered when the array or the object is empty.

index.js

javascript
1app.get("/", (req, res) => {
2  let arr = [];
3  res.render("index", {
4    arr,
5  });
6});

views/index.pug

pug
1doctype html
2html
3  head
4    title Page Title
5  body
6    ul
7      each value in arr
8        li= value
9      else
10        li Array is empty
html
1<!doctype html>
2<html>
3  <head>
4    <title>Page Title</title>
5  </head>
6  <body>
7    <ul>
8      <li>Array is empty</li>
9    </ul>
10  </body>
11</html>

Inheritance

Inheritance is probably one of the most important features of Pug. For most web applications, there are certain components that should remain consistent across multiple pages, such as the navigation menu and the footer.

If you hardcode these components on multiple pages, it will become a huge trouble when you need to make some changes. And it will likely lead to mistakes as you will be jumping across different files.

A better way to do this is to use Pug's inheritance system. For example, you could have a layout.pug file, which contains the code that should be shared on different pages.

views/layout.pug

pug
1doctype html
2html
3  head
4    title Page Title
5    block meta
6  body
7    div Navbar
8    block content
9    div Footer

In this example, using the keyword block, we defined two blocks, meta and content. They will be replaced later by the blocks defined in the children. For instance, you can make our index.pug extend to layout.pug.

views/index.pug

pug
1extends layout.pug
2
3block meta
4  meta(name="description" content="Lorem Ipsum")
5
6block content
7  div This is the content

In this case, line 4 will replace the meta block in the layout.pug, and line 7 will replace the content block, eventually giving you the following result.

index.js

javascript
1app.get("/", (req, res) => {
2  res.render("index");
3});
html
1<!doctype html>
2<html>
3  <head>
4    <title>Page Title</title>
5    <meta name="description" content="Lorem Ipsum" />
6  </head>
7  <body>
8    <div>Navbar</div>
9    <div>This is the content</div>
10    <div>Footer</div>
11  </body>
12</html>

Include

Besides the inheritance system, sometimes you might want to include a piece of HTML in certain pages but not in others, such as a sidebar. In this case, you can use the keyword include like this:

views/sidebar.pug

pug
1div This is the sidebar

views/index.pug

pug
1extends layout.pug
2
3block meta
4  meta(name="description" content="Lorem Ipsum")
5
6block content
7  div This is the content
8  include sidebar.pug
html
1<!doctype html>
2<html>
3  <head>
4    <title>Page Title</title>
5    <meta name="description" content="Lorem Ipsum" />
6  </head>
7  <body>
8    <div>Navbar</div>
9    <div>This is the content</div>
10    <div>This is the sidebar</div>
11    <div>Footer</div>
12  </body>
13</html>

That's not all

In this lesson, we went over some of the most fundamental features of Pug, but there are still some more advanced features we did not cover, such as mixins, filters, and inline JavaScript. These features might be useful to you depending on your specific requirements, so if you are interested, you can still learn more about Pug here.