Skip to content

2. Pug

1. Introduction

As web designers or developers, we likely all have to write our fair share of HTML. And while this is not the most difficult task, it can often feel a little boring or repetitive. This is where the Pug HTML preprocessor comes in.

HTML is also static, which means that if you want to display dynamic data (fetched from an API, for example), you invariably end up with a mishmash of HTML stings inside JavaScript. This can be a nightmare to debug and to maintain. Pug is a template engine for Node and for the browser. It compiles to HTML and has a simplified syntax, which can make you more productive and your code more readable. Pug makes it easy both to write reusable HTML, as well as to render data pulled from a database or API.

1.1 History

It’s also worth noting that Pug used to be called Jade until it was forced to change its name due to a trademark claim in 2015. The name change took effect with version 2.0.

1.2 What is pug used for

A template engine is a program which is responsible for compiling a template (that can be written using any one of a number of languages) into HTML. The template engine will normally receive data from an external source, which it will inject into the template it’s compiling. This is illustrated by the following diagram.

2. Example

Install Pug Package

Terminal window
npm i pug

Create a file hello.pug and place it in .\views folder

  • DirectorytestNodeProject/
    • Directoryviews/
      • hello.pug
    • server.js
    • package.json

In server.js set the view engine to pug

server.js
const express = require('express');
const app = express()
// Set the view engine
app.set( 'view engine', 'pug' ); // by default, it will look for pug templates in views folder
// Optional: To change the location of views to another directory
app.set('views', './views')
// Set handler for the root '/'
app.get('/', (req, res) => {
res.render('hello', {theTitle: 'This is the Title'});
// this will look for the file 'hello.pug' in './views' folder
// second parameter to render(...) method is optional
});
app.listen(3000, function(){
console.log('Listening on port 3000')
});
doctype html
html(lang='en')
head
title Hello, World! #{theTitle || "Default Value"}
//- if `theTitle` parameter is not defined when
//- rending `hello.pug` use the default value "Default Value"
body
h1 Hello, World!
div.remark
p Pug rocks!

Another Example

doctype html
html(lang='en')
head
title Hello, World! #{theTitle || "Default Value"}
//- if `theTitle` parameter is not defined when
//- rending `hello.pug` use the default value "Default Value"
body
div(class="custom", id="myid") Inside div but before paragraph
p paragraph part 01
| paragraph part 02 (continuation)
| outside paragraph but inside div
div.remark
p Pug rocks!

3. DOCTYPE

You can use Pug to generate a number of document type declarations.

For example doctype html will compile to <!DOCTYPE html>, the standard HTML5 doctype, whereas doctype strict will give us <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">. Pug will do its best to ensure that its output is valid for the document type.

4. Tags

As mentioned, Pug doesn’t have any closing tags and relies on indentation for nesting. This might take a small amount of getting used to, but once you do, it makes for clean and readable code. By way of an example:

nav
navbar-default div
h1 My Website!
ul
li
a Home
li
a Page 1
li
a Page 2
input

Rendered output

<nav>
<div>
<h1>My Website!</h1>
</div>
<ul>
<li><a>Home</a></li>
<li><a>Page 1</a></li>
<li><a>Page 2</a></li>
</ul>
<input/>
</nav>

5. Classes, IDs and Attributes

Classes and IDs are expressed using a .className and #IDname notation. For example:

nav#navbar-default
div.container-fluid
h1.navbar-header My Website!

Pug also offers us a handy shortcut. If no tag is specified, it will assume a <div> element:

nav#navbar-default
.container-fluid
h1.navbar-header My Website!

Both of these compile to:

<nav id="navbar-default">
<div class="container-fluid">
<h1 class="navbar-header">My Website!</h1>
</div>
</nav>

5.1 Attributes are added using brackets:

ul
li
a(href='/') Home
li
a(href='/page-1') Page 1
li
a(href='/page-2') Page 2
input.search(
type='text'
name='search'
placeholder='Enter a search term...'
)

This results in the following:

<ul>
<li><a href="/">Home</a></li>
<li><a href="/page-1">Page 1</a></li>
<li><a href="/page-2">Page 2</a></li>
</ul>
<input class="search" type="text" name="search" placeholder="Enter a search term..."/>

6. 🧠 Pug Features

6.1 Variables

h1= title

6.2 Conditionals

if user
p Welcome, #{user.name}!
else
p Please log in.

6.3 Loops

ul
each item in items
li= item

6.3 Mixins (reusable components)

//- In _mixins.pug
mixin card(title, content)
.card
h2= title
p= content
//- In page
include _mixins.pug
+card('Hello', 'This is a card.')

7. Extended Example

Basic Project Structure

  • Directorymy-app/
    • Directoryviews/
      • layout.pug
      • index.pug
      • about.pug
    • Directorypublic/
      • styles.css
    • app.js
    • package.json
app.js
const express = require('express');
const path = require('path');
const app = express();
// Set view engine to Pug
app.set('view engine', 'pug');
app.set('views', path.join(__dirname, 'views'));
// Static files
app.use(express.static(path.join(__dirname, 'public')));
// Routes
app.get('/', (req, res) => {
res.render('index', { title: 'Home', message: 'Welcome to the Pub!' });
});
app.get('/about', (req, res) => {
res.render('about', { title: 'About', team: ['Nadith', 'Jane', 'John'] });
});
// Start server
app.listen(3000, () => console.log('Server started on http://localhost:3000'));

(optional layout template)

layout.pug
doctype html
html
head
title= title
link(rel="stylesheet", href="/styles.css")
body
block content
index.pug
extends layout
block content
h1= message
p This is the home page.
about.pug
extends layout
block content
h1= title
ul
each person in team
li= person

🎨 Static Assets (CSS, Images)

  • Put them in public/
  • Reference with /path in Pug
link(rel="stylesheet", href="/styles.css")
img(src="/logo.png")