Cutting Code Duplication in Pug with Mixins
Duplicate markup hurts maintainability. Pug mixins let you write reusable fragments once, pass data, and keep templates DRY. This post walks through a concrete example, shows how mixins compile, discusses trade‑offs, and gives next steps for production use.
01 Jan 2026, 00:24 UTC

Duplicated markup is a hidden cost
When a project grows, small HTML snippets—buttons, cards, alerts—are copied and pasted across templates. A change to a class name or an attribute then requires hunting every copy. That extra maintenance time and the risk of inconsistent updates can slow delivery and introduce bugs.
Mixins: the Pug answer to DRY templates
A Pug mixin is a reusable block that behaves like a function. You declare the markup once, give it parameters, and invoke it wherever needed. Because mixins are resolved during the compile step, the resulting HTML is identical to hand‑written code, but the source stays concise.
Key features:
- Parameters allow you to customize text, classes, or data.
- Default values keep calls short when a value is common.
- Variadic arguments let you pass an arbitrary number of items.
- Nested mixins support composition, building complex UI components from simpler ones.
- Mixins can be exported from separate files and
included, keeping large codebases modular.
Worked example: a reusable card component
Below is a minimal card mixin that accepts a title, body, and an optional CSS class. The mixin is defined in components/card.pug and used twice in index.pug.
// components/card.pug
mixin card(title, body, className = 'card')
.#{className}
h3= title
p= body
// index.pug
include components/card.pug
+card('Welcome', 'This is the first card.')
+card('Notice', 'Second card with a custom class.', 'alert-card')
Running pug index.pug produces:
<div class="card">
<h3>Welcome</h3>
<p>This is the first card.</p>
</div>
<div class="alert-card">
<h3>Notice</h3>
<p>Second card with a custom class.</p>
</div>
Notice how the same mixin call generates two distinct cards. If you change the h3 tag in the mixin, both cards update automatically.
Trade‑offs and best practices
- Readability vs. abstraction: For trivial elements (e.g., a single
button), a mixin might add indirection. Reserve mixins for components that appear more than twice or that have a clear semantic purpose. - Complex blocks: When a mixin accepts a large block of content (via
block), the template flow can become harder to follow. Keep block parameters well‑named and document them. - Implicit dependencies: Mixing global variables inside a mixin can create hidden coupling. Prefer passing all required data as arguments.
- Testing: Because mixins compile to plain HTML, you can verify output by compiling a test file and inspecting the result, but always guard against accidental changes to default values.
Actionable next steps
- Audit your templates for repeated markup and list candidates for mixins.
- Create a
componentsdirectory and move each candidate into its own file. - Define mixins with clear parameter names and default values where appropriate.
- Replace inline markup with mixin calls and run
pug --pretty index.pugto confirm the output. - Document each mixin in a README or comment block, noting its purpose and parameters.
- Set up a small CI job that compiles key templates and asserts that the output contains expected class names or text fragments.
By following these steps, you’ll reduce duplication, simplify maintenance, and keep your Pug templates clean and expressive.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.