Reusing Pug Layouts with extends and block
Use Pug extends and block to inherit a shared HTML layout and override only the parts that change.
07 Aug 2025, 16:57 UTC

Problem: every page repeats the same HTML skeleton
If you build a site with Pug, it is tempting to copy the doctype, html, head, navigation, and footer into every template. That duplication means a small change—such as adding a stylesheet link or updating a copyright year—requires editing many files. The useful takeaway is to define the shared skeleton once in a parent layout and let page-specific templates fill in only the parts that change.
How Pug template inheritance works
Pug provides two keywords for this: extends and block. A parent layout declares block markers at points where child templates can inject markup. A child template uses extends to inherit that parent, then redefines the blocks it wants to override. Pug compiles each block into a replaceable content function, so the child’s markup is inserted at the parent’s block location without duplicating the surrounding tags.
Assume the project uses Pug 3.x, where extends and block semantics are stable. Earlier releases under the Jade name used the same syntax, but the Node.js runtime API differs, so the examples below target the current Pug package.
Worked configuration: shared layout and a child page
Create a parent layout at views/layout.pug:
doctype html
html
head
meta(charset='utf-8')
title block title Default Site Title
link(rel='stylesheet', href='/styles.css')
body
header
nav
a(href='/') Home
a(href='/about') About
main
block content
footer
p © 2026 Example
The block title and block content lines mark overridable regions. Everything outside those blocks—the doctype, navigation, and footer—is fixed for every page that inherits this layout.
Create a child template at views/index.pug:
extends ./layout.pug
block title
| Homepage
block content
h1 Welcome
p This page inherits the shared layout. Only the title and main content are defined here.
The extends statement must appear before any block definitions. The pipe character (|) passes plain text into the title block.
Rendering the template
Run this from the project directory where Pug is installed:
npm install pug
node -e "const pug = require('pug'); const html = pug.renderFile('views/index.pug', { pretty: true }); console.log(html);"
- Where to run: the application root containing
views/andnode_modules/. - Permissions: read access to
views/layout.pugandviews/index.pug; no write access is required for rendering. - Expected check: the printed HTML should contain
<title>Homepage</title>, an<h1>with “Welcome”, and the inherited navigation and footer. - Risk: an incorrect relative path in
extendsthrows a compile error at render time. A missing block in the parent means content defined in that block may not appear in the output.
Verifying the result
Practical confirmation is to inspect the rendered string rather than assume the template worked. After running the render command, check for these markers in the output:
<title>Homepage</title>proves thetitleblock was overridden.<h1>Welcome</h1>proves thecontentblock was inserted.- The inherited
<nav>and<footer>prove the parent skeleton was preserved.
If any marker is missing, the most likely cause is a block name mismatch between parent and child, or a block defined after non-block markup in the child template.
Limits and common mistakes
Block names must match exactly
If the parent declares block content but the child writes block main, Pug has no matching region to fill. The child’s markup may be dropped or ignored depending on the version, leaving an empty <main> in the rendered page. Match block names character for character.
Do not mix free-form markup with block definitions in a child
Content written directly in the child template outside any block can be discarded or placed in an unexpected position because the parent controls the document structure. Keep page-specific markup inside blocks, or add new blocks to the parent first.
Nested inheritance adds complexity
A parent layout can itself use extends to inherit from a more base layout. That chain works, but each intermediate parent must yield blocks explicitly rather than hardcoding text where a child expects to override. Chains deeper than two levels are harder to debug and should be avoided unless the layout variation genuinely requires it.
Unescaped output can interfere with blocks
Using != for unescaped output inside a parent block can override or bypass the child’s block content in some render paths. Prefer escaped output (=) inside block markers unless you specifically need raw HTML and understand the ordering implications.
Practical checklist before deploying
- Confirm every
blockin a child template has a same-namedblockin the parent. - Render each child template and grep the output for the expected title and content strings.
- Change a shared element in the parent—such as the footer year—and confirm the update appears in every child without editing them individually.
- Pin the Pug version in
package.jsonbecause runtime API behavior changed between Jade-era releases and Pug 3.x.
Template inheritance in Pug is a compile-time composition mechanism, not a runtime include. Once the parent and child blocks are aligned, adding a new page means writing only the page-specific blocks while the shared HTML skeleton stays in one file.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.