Reducing Template Duplication with Pug Mixins
Learn how to implement Pug mixins to create reusable UI components, reduce HTML duplication, and manage complex template structures in Node.js environments.
13 Jan 2026, 09:56 UTC

The Problem: HTML Bloat in Dynamic Templates
When building layouts in Pug, developers often find themselves copying and pasting the same HTML structures—such as cards, form inputs, or navigation items—across multiple files. This repetition makes updates tedious and increases the risk of inconsistent UI elements across a project.
The solution is the Mixin. A mixin is a reusable block of template code that functions like a function in JavaScript. It allows you to define a UI pattern once and inject it anywhere in your render stream with unique data.
Prerequisites
- Node.js installed on your development machine.
- The
pugpackage installed via npm (npm install pug). - A basic understanding of Pug indentation and syntax.
Defining and Implementing Mixins
Mixins are defined using the mixin keyword. They can accept parameters to make the output dynamic and use the block keyword to allow the caller to inject custom HTML inside the mixin's structure.
Step 1: Create a Reusable Component
Create a file named components.pug. In this example, we will build a product card that handles both simple text and custom action buttons.
//- components.pug
mixin productCard(title, price, description)
.card
.card-header
h3= title
span.price ${price}
.card-body
p= description
.card-footer
//- The block keyword allows us to pass custom HTML from the caller
block
Step 2: Integrating the Mixin into a Page
To use the mixin, you must first include the file where the mixin is defined. Then, call the mixin using the + prefix.
//- index.pug
include components.pug
html
body
h1 Store Catalog
.grid
//- Call 1: Basic usage
+productCard('Mechanical Keyboard', 120, 'RGB backlit with brown switches')
//- Call 2: Usage with a custom block for a button
+productCard('Gaming Mouse', 60, '12,000 DPI optical sensor')
button.btn-buy Add to Cart
button.btn-wishlist Save for Later
//- Call 3: Different data, no block
+productCard('Ultrawide Monitor', 450, '34-inch curved display')
Diagnostic Decision: Parameters vs. Blocks
A common point of confusion is when to use a parameter versus a block. Use the following logic to decide:
| Scenario | Recommended Approach | Reasoning |
|---|---|---|
| Changing text, colors, or IDs | Parameters | Fast, type-safe, and keeps the calling code clean. |
| Changing the internal HTML structure | Blocks | Allows the caller to define complex nested elements without overloading the mixin signature. |
Execution and Verification
To verify the implementation, render the Pug file to HTML. If you are using the Pug CLI, run the following command in your terminal:
pug index.pug -o ./distPermissions: Ensure the user running the command has write permissions for the ./dist directory.
Verification Checklist
- Output Consistency: Open the generated HTML. Ensure all three
.cardelements share the same CSS classes and structure. - Block Rendering: Verify that the 'Gaming Mouse' card contains the two buttons, while the others do not.
- Indentation: Check that the generated HTML is not nested deeper than intended; Pug mixins should maintain the indentation level of the call site.
Limitations and Risks
- Stack Trace Complexity: Deeply nesting mixins (a mixin calling another mixin calling another) can make runtime error messages difficult to trace back to the original source line.
- Opaque Signatures: Avoid passing a single large object (e.g.,
+productCard(productObj)) as the only argument. This hides the mixin's requirements from other developers. Prefer named parameters for clarity. - Compile-time Processing: Mixins are processed during the compilation phase. You cannot dynamically define new mixins at runtime based on user input.
Rollback
Because mixins are a templating syntax and do not alter database state or system configurations, "rollback" consists of reverting the .pug file to its previous state via your version control system (e.g., git checkout index.pug) and re-running the build command.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.