Skip to main content

Express Pug Integration

Introduction

Pug (formerly known as Jade) is a powerful template engine for Node.js that integrates seamlessly with Express.js. It provides a clean, whitespace-sensitive syntax for writing HTML templates that can dramatically reduce the amount of code you need to write. In this tutorial, we'll explore how to integrate Pug with Express and leverage its features to create dynamic web pages.

Pug allows you to:

  • Write HTML with a simplified, indentation-based syntax
  • Use variables, conditionals, and loops in your templates
  • Create reusable components through includes and mixins
  • Generate HTML dynamically based on data from your Express routes

Getting Started with Pug

Installation

To start using Pug with Express, you first need to install it as a dependency in your project:

bash
npm install pug

Basic Configuration

After installing Pug, you need to configure Express to use it as the template engine:

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

// Set Pug as the view engine
app.set('view engine', 'pug');

// Specify the directory where your Pug templates will be stored
app.set('views', './views');

// Start the server
app.listen(3000, () => {
console.log('Server running on port 3000');
});

This tells Express to use Pug for rendering templates and look for template files in the ./views directory.

Creating Your First Pug Template

Let's create a simple Pug template. Create a new file called index.pug in your views directory:

pug
doctype html
html
head
title My Pug Template
style.
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
}
body
.container
h1 Welcome to my Pug template!
p This is a paragraph in Pug. It's much cleaner than HTML!

Now create a route in your Express app to render this template:

javascript
app.get('/', (req, res) => {
res.render('index');
});

When you visit http://localhost:3000/, Express will render the index.pug template into HTML and send it to the browser. The resulting HTML will look like this:

html
<!DOCTYPE html>
<html>
<head>
<title>My Pug Template</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<h1>Welcome to my Pug template!</h1>
<p>This is a paragraph in Pug. It's much cleaner than HTML!</p>
</div>
</body>
</html>

Passing Data to Templates

One of the most powerful features of template engines like Pug is the ability to pass data from your Express routes to your templates.

Basic Variable Usage

javascript
app.get('/user', (req, res) => {
res.render('user', {
name: 'John Doe',
email: '[email protected]',
active: true
});
});

Then in your user.pug file:

pug
doctype html
html
head
title User Profile
body
h1 User Details
if active
p.status User is active
else
p.status User is inactive

dl
dt Name:
dd= name
dt Email:
dd= email

The output HTML will be:

html
<!DOCTYPE html>
<html>
<head>
<title>User Profile</title>
</head>
<body>
<h1>User Details</h1>
<p class="status">User is active</p>
<dl>
<dt>Name:</dt>
<dd>John Doe</dd>
<dt>Email:</dt>
<dd>[email protected]</dd>
</dl>
</body>
</html>

Working with Arrays and Loops

Pug makes it easy to iterate over arrays and render dynamic content:

javascript
app.get('/products', (req, res) => {
const products = [
{ id: 1, name: 'Laptop', price: 999.99 },
{ id: 2, name: 'Smartphone', price: 699.99 },
{ id: 3, name: 'Tablet', price: 399.99 }
];

res.render('products', { products });
});

Then in your products.pug file:

pug
doctype html
html
head
title Product List
style.
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
body
h1 Our Products

table
thead
tr
th ID
th Product Name
th Price
tbody
each product in products
tr
td= product.id
td= product.name
td $#{product.price.toFixed(2)}

p Total products: #{products.length}

Template Inheritance and Layout

Pug supports template inheritance, which lets you define a base layout and extend it in child templates.

Base Layout

Create a file called layout.pug:

pug
doctype html
html
head
title #{title} - My Website
block styles
link(rel='stylesheet', href='/css/style.css')
body
header
h1 My Website
nav
ul
li: a(href='/') Home
li: a(href='/about') About
li: a(href='/contact') Contact

.content
block content

footer
p &copy; 2023 My Website. All rights reserved.

Child Template

Now create a child template that extends the layout, for example about.pug:

pug
extends layout

block styles
link(rel='stylesheet', href='/css/about.css')

block content
h2 About Us
p Welcome to our company page! We are dedicated to providing excellent service.

.team
h3 Our Team
ul
each member in team
li
strong #{member.name} -
span #{member.role}

Then in your Express route:

javascript
app.get('/about', (req, res) => {
res.render('about', {
title: 'About Us',
team: [
{ name: 'Alice', role: 'CEO' },
{ name: 'Bob', role: 'CTO' },
{ name: 'Charlie', role: 'Designer' }
]
});
});

Includes and Partials

For reusable components, Pug offers the include directive, which lets you include one template inside another.

Creating a Partial

Create a file called _nav.pug for your navigation menu:

pug
nav.main-nav
ul
li: a(href='/') Home
li: a(href='/products') Products
li: a(href='/services') Services
li: a(href='/contact') Contact

Using the Partial

Now you can include this partial in any template:

pug
doctype html
html
head
title Site Navigation
body
header
h1 My Website
include _nav

.content
h2 Welcome to our website!

Mixins: Reusable Template Functions

Mixins are like functions in your templates that can accept arguments and be reused.

Defining a Mixin

Create a file called _mixins.pug:

pug
mixin card(title, content, imgSrc)
.card
if imgSrc
img.card-img(src=imgSrc, alt=title)
.card-body
h3.card-title= title
p.card-text= content
if block
.card-actions
block

mixin alert(type, message)
.alert(class='alert-' + type)
strong= type.charAt(0).toUpperCase() + type.slice(1) + ': '
= message

Using Mixins

pug
include _mixins

doctype html
html
head
title Using Mixins
body
h1 Featured Content

+alert('success', 'Your profile has been updated!')

.cards
+card('Getting Started', 'Learn how to use our platform with this quick guide', '/img/getting-started.jpg')
a.btn(href='/guide') Read More

+card('Premium Features', 'Discover our premium features', '/img/premium.jpg')
a.btn.primary(href='/premium') Upgrade Now

Real-World Example: A Blog with Express and Pug

Let's create a simple blog application using Express and Pug. First, we'll set up our folder structure:

blog-app/
├── app.js
├── package.json
├── public/
│ ├── css/
│ │ └── style.css
│ └── img/
└── views/
├── layout.pug
├── index.pug
├── post.pug
└── partials/
├── _header.pug
└── _footer.pug

Main Express Application (app.js)

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

// Configure Express
app.set('view engine', 'pug');
app.set('views', path.join(__dirname, 'views'));

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

// Mock database of blog posts
const posts = [
{
id: 1,
title: 'Getting Started with Express',
slug: 'getting-started-with-express',
date: '2023-04-10',
author: 'Jane Doe',
content: 'Express is a minimal and flexible Node.js web application framework...',
excerpt: 'Learn how to build web applications with Express.js'
},
{
id: 2,
title: 'Templating with Pug',
slug: 'templating-with-pug',
date: '2023-04-15',
author: 'John Smith',
content: 'Pug is a high-performance template engine heavily influenced by Haml...',
excerpt: 'Discover how to create templates with Pug'
}
];

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

// Single post route
app.get('/post/:slug', (req, res) => {
const post = posts.find(p => p.slug === req.params.slug);

if (!post) {
return res.status(404).render('error', {
title: 'Post Not Found',
message: 'The requested post could not be found'
});
}

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

// Start the server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

Layout Template (layout.pug)

pug
doctype html
html(lang="en")
head
meta(charset="UTF-8")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
title #{title} | Express Blog
link(rel="stylesheet", href="/css/style.css")
body
include partials/_header

main.container
block content

include partials/_footer

block scripts

Home Page (index.pug)

pug
extends layout

block content
h1 Express Blog
p Welcome to our blog! Read our latest posts below.

.posts
each post in posts
article.post-preview
h2: a(href=`/post/${post.slug}`)= post.title
.meta
span.date= post.date
span.author by #{post.author}
p.excerpt= post.excerpt
a.read-more(href=`/post/${post.slug}`) Read More →
else
p No posts found.

Single Post Page (post.pug)

pug
extends layout

block content
article.post
h1= post.title

.meta
p
strong Date:
time= post.date
p
strong Author:
span= post.author

.post-content
p= post.content

hr

.post-nav
a(href="/") ← Back to All Posts

Header Partial (_header.pug)

pug
header.site-header
.container
a.site-title(href="/") Express Blog
nav.main-nav
ul
li: a(href="/") Home
li: a(href="/about") About
li: a(href="/contact") Contact
pug
footer.site-footer
.container
p &copy; 2023 Express Blog. Built with Express and Pug.

This example demonstrates a basic blog with routing, template inheritance, and partials. You can expand it with additional features like pagination, comments, categories, etc.

Best Practices for Using Pug with Express

  1. Organize templates logically: Use folders to group related templates and partials.

  2. Use layout templates: Don't repeat common elements like headers and footers in every template.

  3. Create reusable components: Use mixins and includes to avoid repeating code.

  4. Keep logic minimal: Minimize logic in templates; do complex computations in your Express routes.

  5. Use proper indentation: Since Pug is whitespace-sensitive, consistent indentation is crucial.

  6. Comment your code: Use Pug comments (//- This is a comment) for clarity.

  7. Handle errors gracefully: Create error templates for different HTTP error codes.

  8. Cache templates in production: Use Express's built-in template caching in production:

javascript
if (process.env.NODE_ENV === 'production') {
app.set('view cache', true);
}

Common Gotchas and Troubleshooting

  1. Indentation issues: Make sure your indentation is consistent, as Pug is whitespace-sensitive.

  2. Missing closing tags: Unlike HTML, Pug doesn't need closing tags, but improper indentation can cause unexpected output.

  3. Escaping content: By default, Pug escapes variables to prevent XSS. To render unescaped HTML, use !{variable} instead of #{variable}.

  4. Path resolution: Make sure your include/extend paths are correct. Paths are relative to the file where they're used unless you configure it otherwise.

  5. Template not found errors: Double-check that your view folder configuration matches your actual folder structure.

Summary

In this tutorial, we've explored how to integrate Pug with Express.js to create dynamic web pages. We've covered:

  • Setting up Pug as Express's template engine
  • Creating basic templates and passing data from routes to templates
  • Using conditionals, loops, and variables in templates
  • Implementing template inheritance with layouts
  • Creating reusable components with includes and mixins
  • Building a real-world blog application with Express and Pug
  • Best practices and common pitfalls

Pug's simplified syntax and powerful features make it an excellent choice for building dynamic web applications with Express. By leveraging template inheritance, partials, and mixins, you can create maintainable and DRY templates that are easy to work with.

Additional Resources

Exercises

  1. Create a simple to-do list application using Express and Pug that allows users to view, add, and mark tasks as complete.

  2. Build a portfolio website template with multiple pages (home, about, projects, contact) using Pug layouts and partials.

  3. Create a product catalog with categories and product details pages. Use mixins to generate product cards.

  4. Extend the blog example with pagination, categories, and a search function.

  5. Create a dashboard layout with a sidebar navigation and multiple content views.



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