A clean, whitespace-sensitive template language for writing HTML
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Native Express.js view engine support for seamless web application integration. Pug provides a built-in Express view engine that handles template rendering within the Express middleware pipeline.
The __express function provides Express.js view engine interface compatibility.
/**
* Express.js view engine interface
* @param path - Template file path
* @param options - View options from Express including locals
* @param fn - Express callback function
*/
function __express(path, options, fn);Usage Examples:
const express = require('express');
const pug = require('pug');
const app = express();
// Set Pug as the view engine
app.set('view engine', 'pug');
app.set('views', './views');
// Express routes using Pug templates
app.get('/', (req, res) => {
res.render('index', {
title: 'Home Page',
message: 'Welcome to our site!'
});
});
app.get('/user/:id', (req, res) => {
const user = getUserById(req.params.id);
res.render('user-profile', {
user: user,
isOwner: req.user && req.user.id === user.id
});
});
app.listen(3000);Basic Setup:
const express = require('express');
const app = express();
// Configure Pug as view engine
app.set('view engine', 'pug');
app.set('views', path.join(__dirname, 'views'));
// Optional: Configure Pug-specific settings
app.locals.pretty = true; // Pretty-print HTML (development only)
app.locals.cache = process.env.NODE_ENV === 'production';Advanced Configuration:
const express = require('express');
const pug = require('pug');
const app = express();
// Custom Pug configuration
app.engine('pug', (filePath, options, callback) => {
// Custom options for all Pug templates
const pugOptions = {
...options,
basedir: app.get('views'),
cache: app.get('env') === 'production',
compileDebug: app.get('env') !== 'production',
filters: {
markdown: (text) => require('markdown-it')().render(text),
currency: (amount) => '$' + parseFloat(amount).toFixed(2)
}
};
return pug.renderFile(filePath, pugOptions, callback);
});
app.set('view engine', 'pug');Typical Express + Pug project structure:
project/
├── views/
│ ├── layout.pug # Base layout template
│ ├── index.pug # Home page
│ ├── error.pug # Error page
│ ├── partials/
│ │ ├── header.pug # Header component
│ │ ├── footer.pug # Footer component
│ │ └── nav.pug # Navigation
│ └── users/
│ ├── profile.pug # User profile
│ └── settings.pug # User settings
├── public/ # Static assets
├── routes/ # Express routes
└── app.js # Main applicationLayout Template (views/layout.pug):
doctype html
html
head
title= title || 'My App'
link(rel='stylesheet', href='/css/style.css')
block head
body
include partials/header
main.container
block content
include partials/footer
script(src='/js/app.js')
block scriptsPage Template (views/index.pug):
extends layout
block head
meta(name='description', content='Welcome to our homepage')
block content
h1= title
p= message
if user
p Welcome back, #{user.name}!
else
a(href='/login') Please log in
block scripts
script(src='/js/homepage.js')Template Variables Middleware:
// Add common variables to all templates
app.use((req, res, next) => {
res.locals.currentUser = req.user;
res.locals.currentPath = req.path;
res.locals.environment = app.get('env');
res.locals.moment = require('moment'); // Date utility
next();
});
// Now available in all templates
app.get('/dashboard', (req, res) => {
res.render('dashboard', {
title: 'Dashboard'
// currentUser, currentPath, environment, moment are automatically available
});
});Error Handling Middleware:
// Custom error page rendering
app.use((err, req, res, next) => {
res.status(err.status || 500);
if (req.accepts('html')) {
res.render('error', {
title: 'Error',
error: app.get('env') === 'development' ? err : {},
message: err.message,
status: err.status || 500
});
} else if (req.accepts('json')) {
res.json({ error: err.message });
} else {
res.type('txt').send(err.message);
}
});Conditional Rendering:
app.get('/products', (req, res) => {
const products = getProducts();
const viewMode = req.query.view || 'grid';
res.render('products', {
title: 'Products',
products: products,
viewMode: viewMode,
showFilters: products.length > 10,
isAdmin: req.user && req.user.role === 'admin'
});
});Template Caching Strategy:
const express = require('express');
const app = express();
// Environment-based caching
if (app.get('env') === 'production') {
app.enable('view cache'); // Enable Express view caching
app.locals.cache = true; // Enable Pug template caching
app.locals.compileDebug = false;
} else {
app.locals.pretty = true; // Pretty HTML in development
app.locals.compileDebug = true;
}Custom Helper Functions:
// Add helper functions to templates
app.locals.formatDate = (date) => {
return new Date(date).toLocaleDateString();
};
app.locals.truncate = (text, length = 100) => {
return text.length > length ? text.substring(0, length) + '...' : text;
};
app.locals.asset = (path) => {
// Asset versioning helper
const version = app.get('env') === 'production' ? '?v=1.0.0' : '';
return path + version;
};
// Usage in templates:
// p= formatDate(post.createdAt)
// p= truncate(post.content, 150)
// link(rel='stylesheet', href=asset('/css/style.css'))Production Configuration:
const express = require('express');
const app = express();
if (process.env.NODE_ENV === 'production') {
// Enable all Express optimizations
app.enable('view cache');
app.set('trust proxy', 1);
// Pug optimizations
app.locals.cache = true;
app.locals.compileDebug = false;
app.locals.pretty = false;
// Precompile critical templates
const criticalTemplates = ['layout', 'index', 'error'];
criticalTemplates.forEach(template => {
pug.compileFile(`./views/${template}.pug`, { cache: true });
});
}Memory Management:
// Monitor template cache size in production
if (process.env.NODE_ENV === 'production') {
setInterval(() => {
const cacheSize = Object.keys(pug.cache).length;
console.log(`Template cache size: ${cacheSize}`);
if (cacheSize > 100) {
console.warn('Template cache is large, consider cleanup');
}
}, 300000); // Check every 5 minutes
}Template Rendering Errors:
app.get('/user/:id', (req, res, next) => {
try {
const user = getUserById(req.params.id);
if (!user) {
return res.status(404).render('404', {
title: 'User Not Found',
message: 'The requested user could not be found.'
});
}
res.render('user-profile', { user });
} catch (err) {
// Template rendering error
console.error('Template error:', err);
next(err);
}
});Development Error Pages:
if (app.get('env') === 'development') {
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.render('error', {
title: 'Development Error',
message: err.message,
error: err, // Full error object in development
stack: err.stack
});
});
}Template Testing Setup:
const request = require('supertest');
const express = require('express');
const app = express();
app.set('view engine', 'pug');
app.set('views', './test/fixtures/views');
app.get('/test', (req, res) => {
res.render('test-template', { message: 'Hello Test' });
});
// Test template rendering
describe('Template Rendering', () => {
it('should render test template', (done) => {
request(app)
.get('/test')
.expect(200)
.expect(/<p>Hello Test<\/p>/)
.end(done);
});
});Mocking Template Data:
const mockData = {
users: [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
]
};
app.get('/test-users', (req, res) => {
res.render('users', {
title: 'Test Users',
users: mockData.users
});
});This completes the Express.js integration documentation, providing comprehensive coverage of how to use Pug as an Express view engine with practical examples and best practices.
Install with Tessl CLI
npx tessl i tessl/npm-pug