Pug Template Inheritance: extends, block, and Version-Safe Patterns
Use Pug extends and block to share layouts without duplicating markup, but verify your Pug version—append and prepend behavior changed between releases.
12 Jul 2025, 02:06 UTC

The problem: your layout edit didn’t reach the page
You update layout.pug, restart the server, and the rendered page still shows the old markup. Or you add a page-specific script with append, and it lands in the wrong position. Pug’s template inheritance is compile-time, not runtime, so editing a base layout does not automatically refresh already-compiled templates. This post shows how extends and block resolve, how to verify behavior against your installed Pug version, and what to check when inherited output looks wrong.
How extends and block resolve at compile time
Pug is a Node.js template engine: it compiles .pug files into JavaScript functions, then calls those functions with a rendering context to produce HTML. extends tells Pug that one template inherits from another. block names a replaceable section in the parent. Inheritance is resolved when the template is compiled, not when the page is requested. If your application caches compiled functions, you must invalidate that cache after changing a base layout.
Important terms: compile time is when Pug turns source into a JavaScript function. Render time is when that function runs with variables to produce HTML. Rendering context is the data object passed to the compiled function.
Worked example: base layout and child page
Create views/layout.pug:
doctype html
html
head
title My Site
block styles
link(rel="stylesheet", href="/css/global.css")
body
block content
block scripts
script(src="/js/global.js")
Create views/page.pug. extends must be the first construct in the file; anything before it can cause a compile error.
extends layout.pug
block content
h1 Welcome
p This page inherits the base layout.
block scripts
script(src="/js/page.js")
Render it from a Node.js script (no special permissions required):
const pug = require('pug');
const fs = require('fs');
const page = fs.readFileSync('views/page.pug', 'utf8');
// Compile the child; Pug resolves extends against layout.pug at compile time.
const compiled = pug.compile(page, { filename: 'views/page.pug', basedir: 'views' });
const html = compiled({});
console.log(html);
Run with node render.js from the project root. Expected check: the h1 appears inside body, and /js/page.js appears in the output. Risk: if the child omits block scripts, the parent’s default script may still render, or the section may be empty depending on the Pug major version.
append and prepend: version-sensitive behavior
Use append or prepend inside a child block to add to the parent’s content instead of replacing it. This is useful for page-specific stylesheets or JavaScript bundles.
block styles
append
link(rel="stylesheet", href="/css/page.css")
In Pug v2 and v3, the exact position of appended or prepended content relative to the parent block can differ. Before relying on the order, check the installed release:
npx pug --version
Or in Node:
console.log(require('pug/package.json').version);
Required permission: none. Expected check: the first number should be 2 or 3. If you are upgrading from v2 to v3, render a test page with append and inspect the HTML to confirm script or stylesheet order matches your expectation.
Limitations and how to verify the result
- Block names must match exactly. A typo creates a new block instead of overriding the intended one, which can produce silently empty output in some versions.
- Compile-time resolution means base-layout changes require a restart or cache invalidation in production.
- Inheritance couples pages to a base layout, making incremental migration of static HTML more difficult.
Practical verification: render layout.pug with a child that defines block content h1 Hello, then confirm the heading appears inside body in the output. Next, define one script tag in the base block scripts, append another in the child, and inspect the rendered HTML to verify the order. If the output does not match, check pug --version and review the block spelling in both files.
Closing
Pug inheritance works best when you treat the base layout as a contract: named blocks, exact spelling, and a known Pug version. Compile your templates in a test script, inspect the HTML, and clear the template cache after layout changes. If append order matters, pin your Pug version and verify it after upgrades.