Installing and building
Pug is an npm package with functions for Node.js. You write templates in .pug files, and a short script calls Pug and writes finished .html files. You need Node.js 20 or newer.
Installing
Create a folder for the site and install Pug in it as a development dependency:
mkdir cafe
cd cafe
npm init -y
npm pkg set type=module
npm install --save-dev pugnpm init -y creates package.json, and npm pkg set type=module turns on ES modules in the project so you can write import instead of require.
Project structure
Templates live in src/, finished pages go to dist/:
cafe/
├── build.js ← build script
├── dist/
│ └── index.html ← created by Pug
├── src/
│ └── index.pug ← edited by you
└── package.jsonCreate the file src/index.pug:
h1 Morning Café
p Fresh coffee every day from 8:00.Two lines, two tags: a heading and a paragraph with text.
The build script
Create the file build.js in the project root:
// build.js
import { mkdirSync, writeFileSync } from "node:fs";
import pug from "pug";
const html = pug.renderFile("src/index.pug", { pretty: true });
mkdirSync("dist", { recursive: true });
writeFileSync("dist/index.html", html);
console.log("Done: dist/index.html");pug.renderFile() reads the template and returns a string of HTML. The second argument holds options: pretty: true adds line breaks and indentation so the result is easy to read. Without it, Pug writes the whole page on one line — the browser does not care, and the file is smaller.
Running it
node build.jsThe terminal prints "Done: dist/index.html", and the file contains this HTML:
<h1>Morning Café</h1>
<p>Fresh coffee every day from 8:00.</p>Open dist/index.html in the browser and you will see the heading and the paragraph.
To avoid typing the whole command, add it to package.json and run npm run build:
{
"scripts": {
"build": "node build.js"
}
}Node.js watch mode helps rebuild pages after every edit: node --watch-path=src build.js runs the script again whenever anything in the src/ folder changes.
What about pug-cli?
Older guides run Pug with the pug command from the pug-cli package. That package has not been updated for a long time and uses Pug 2 inside, so this course uses its own script: it runs Pug 3 and is easy to extend once there are several pages.
Common mistake
An edit in src/index.pug does not show up in the browser, because the browser opens dist/index.html, which does not change by itself. After every template edit run npm run build, or keep watch mode running.