Express.js Setting Up Pug

Pug (formerly known as Jade) is a high-performance templating engine for Node.js. It is widely loved by Express developers because it transforms the verbose, bracket-heavy syntax of standard HTML into a clean, indented format. By using Pug, you can write significantly less code while maintaining better readability.

Developer Tip: Most modern IDEs like VS Code require an extension (like "Pug Language Support") to provide syntax highlighting and autocomplete for .pug files.

 

Key Features of Pug

  • Concise Syntax: Pug relies on indentation rather than closing tags (like </div>). This reduces "tag soup" and makes the structure of your page obvious at a glance.
  • Dynamic Content: It bridges the gap between your JavaScript logic and your HTML, allowing you to inject variables directly into the view.
  • Template Inheritance: This is a powerful feature that lets you define a "master layout" (with your head, navbar, and footer) and simply swap out the content for different pages.
  • Partials: You can break your code into small, manageable chunks—like a separate file for a sidebar—and include them wherever needed.

 

Steps to Set Up Pug in Express.js

Step 1: Install Pug
First, you need to add the Pug package to your project dependencies. Open your terminal in your project root and run:

npm install pug
Watch Out: Pug is whitespace-sensitive. If you mix tabs and spaces for indentation, your code will throw an error. Pick one (usually 2 spaces) and stick with it throughout your project.

Step 2: Set Pug as the View Engine
Express needs to know which engine to use to process your templates. You configure this in your main server file (usually app.js or server.js):

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

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

// Tell Express where your template files are located
app.set('views', path.join(__dirname, 'views'));
Best Practice: Use path.join(__dirname, 'views') instead of a hardcoded string like './views'. This ensures your application finds the folder correctly regardless of which directory you start the node process from.

Step 3: Create the Views Folder
Create a folder named views in your project root. This is where Express will look for your templates by default.

Step 4: Create Your First Pug Template
Inside the views folder, create a file named index.pug. Notice how we use indentation instead of brackets:

doctype html
html
  head
    title= title
  body
    h1 Welcome to #{title}!
    p This page was rendered using the Pug engine.
Common Mistake: Beginners often confuse title= title and #{title}. Use tag= variable for escaping attributes/content entirely, and #{variable} for "interpolation" (mixing text and variables together).

Step 5: Render Views in Express Routes
Now, tell your route to "render" the file. Express will automatically look in the views folder and append the .pug extension.

app.get('/', (req, res) => {
    // We pass an object containing our dynamic data
    res.render('index', { title: 'My Express Application' });
});

Step 6: Run the Application
Start your server:

node app.js

Navigate to http://localhost:3000. You’ll see that Pug has compiled your shorthand code into standard HTML for the browser to read.

 

Using Template Inheritance in Pug

Template inheritance allows you to build a "skeleton" for your site so you don't have to rewrite the same HTML boilerplate on every single page.

Create a Layout Template (layout.pug)
The block content keyword acts as a placeholder where child pages will "inject" their specific code.

doctype html
html
  head
    title My Website
    link(rel="stylesheet", href="/css/style.css")
  body
    header
      nav
        a(href="/") Home
        a(href="/about") About
    
    main
      block content

    footer
      p © 2026 My Website

Extend the Layout (index.pug)
Now, your actual pages only need to focus on their unique content:

extends layout

block content
  h2 Welcome to the Homepage!
  p This content is being injected into the main layout template.

 

Passing Data and Logic to Pug

Pug isn't just for static text; you can pass complex objects and even use JavaScript logic like loops and conditionals.

Example: Rendering a List

app.get('/users', (req, res) => {
    const users = ['Alice', 'Bob', 'Charlie'];
    res.render('users', { userList: users });
});

In views/users.pug, you can loop through the array easily:

extends layout

block content
  h1 User Directory
  ul
    each user in userList
      li= user
    else
      li No users found.
Developer Tip: The else block inside an each loop in Pug is incredibly useful—it runs automatically if the array is empty.

 

Using Partials in Pug

Partials are perfect for UI components like buttons, contact forms, or navigation bars that appear on multiple pages but aren't necessarily part of the main layout.

Create a Partial (views/includes/nav.pug):

nav.main-navigation
  ul
    li: h3 Navigation
    li: a(href="/") Home
    li: a(href="/contact") Contact

Include the Partial:

body
  include includes/nav
  h1 Main Page Content

 

Using Helpers in Pug

Sometimes you need to format data (like dates or currency) before displaying it. You can pass functions from your route directly into the view.

app.get('/profile', (req, res) => {
    const helpers = {
        shout: (text) => text.toUpperCase()
    };
    res.render('profile', { name: 'John Doe', ...helpers });
});

In views/profile.pug:

h1 User Profile
p User Name: #{shout(name)}
Best Practice: For functions you want to use on every page, use app.locals. For example: app.locals.formatPrice = (num) => '$' + num.toFixed(2);. This makes the function available in all Pug templates without passing it manually in every route.

 

Summary

Pug is a powerful ally for Express.js developers, offering a "write less, do more" approach to HTML. By mastering indentation, template inheritance, and partials, you can build modular, maintainable front-ends with very little overhead. Whether you are building a simple blog or a complex dashboard, Pug's clean syntax helps you stay focused on your application logic rather than wrestling with closing HTML tags.