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:
npm install pug
Basic Configuration
After installing Pug, you need to configure Express to use it as the template engine:
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:
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:
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:
<!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
app.get('/user', (req, res) => {
res.render('user', {
name: 'John Doe',
email: '[email protected]',
active: true
});
});
Then in your user.pug
file:
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:
<!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:
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:
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
:
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 © 2023 My Website. All rights reserved.
Child Template
Now create a child template that extends the layout, for example about.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:
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:
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:
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
:
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
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)
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)
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)
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)
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)
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
Footer Partial (_footer.pug)
footer.site-footer
.container
p © 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
-
Organize templates logically: Use folders to group related templates and partials.
-
Use layout templates: Don't repeat common elements like headers and footers in every template.
-
Create reusable components: Use mixins and includes to avoid repeating code.
-
Keep logic minimal: Minimize logic in templates; do complex computations in your Express routes.
-
Use proper indentation: Since Pug is whitespace-sensitive, consistent indentation is crucial.
-
Comment your code: Use Pug comments (
//- This is a comment
) for clarity. -
Handle errors gracefully: Create error templates for different HTTP error codes.
-
Cache templates in production: Use Express's built-in template caching in production:
if (process.env.NODE_ENV === 'production') {
app.set('view cache', true);
}
Common Gotchas and Troubleshooting
-
Indentation issues: Make sure your indentation is consistent, as Pug is whitespace-sensitive.
-
Missing closing tags: Unlike HTML, Pug doesn't need closing tags, but improper indentation can cause unexpected output.
-
Escaping content: By default, Pug escapes variables to prevent XSS. To render unescaped HTML, use
!{variable}
instead of#{variable}
. -
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.
-
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
- Official Pug Documentation
- Express.js Documentation on Template Engines
- Pug to HTML Converter (useful for learning Pug syntax)
Exercises
-
Create a simple to-do list application using Express and Pug that allows users to view, add, and mark tasks as complete.
-
Build a portfolio website template with multiple pages (home, about, projects, contact) using Pug layouts and partials.
-
Create a product catalog with categories and product details pages. Use mixins to generate product cards.
-
Extend the blog example with pagination, categories, and a search function.
-
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! :)