Reducing Markup Repetition with Pug Mixins
Stop duplicating HTML in your Pug templates. Learn how to use mixins to create reusable, parameterized markup fragments and wrapper elements for cleaner, maintainable code.
23 Feb 2026, 10:33 UTC

The Problem: The 'Copy-Paste' Template Trap
When building a site with Pug, it is easy to fall into a pattern of duplicating HTML structures. You might have a specific style for a card, a form input, or a button that appears ten times across three different pages. Every time the design changes—for example, adding a new CSS class to all buttons—you have to find and replace every instance manually.
The solution is the mixin. A mixin is essentially a reusable function for your markup. Instead of copying HTML, you define a blueprint once and call it wherever needed, passing in specific data to customize the output.
Defining and Invoking Mixins
A mixin is declared using the mixin keyword followed by a name and optional parameters. To use the mixin later in your template, you invoke it using the + prefix.
Mixins create their own local scope. Variables defined inside a mixin do not leak into the rest of your template, which prevents naming collisions when your project grows.
Example: A Reusable Form Input
// Define the mixin
mixin formField(labelName, type = 'text', placeholder = '')
.field-group
label(for=labelName)= labelName
input(type=type, id=labelName, name=labelName, placeholder=placeholder)
if error && error[labelName]
span.error= error[labelName]
// Use the mixin in your template
form(method='POST' action='/submit')
+formField('username', 'text', 'Enter your username')
+formField('password', 'password', 'Enter your password')
+formField('email', 'email', '[contact removed]')
button(type='submit') Submit
In this example, type and placeholder have default values. If you only provide the labelName, Pug fills in the rest automatically.
Handling Dynamic Content with Blocks
Sometimes a mixin needs to be a \"wrapper\" rather than just a snippet. For instance, a modal or a card might have a consistent border and header, but the content inside varies wildly. For this, Pug provides the block keyword.
When you call a mixin and indent content beneath it, that content is passed into the mixin as a block. The mixin can then decide exactly where to render that content using the block keyword.
Example: A Content Wrapper
mixin card(title)
.card
.card-header
h3= title
.card-body
block
// Using the card mixin with custom inner HTML
+card('User Profile')
p Name: Jane Doe
p Role: Engineer
button Click for Details
+card('System Status')
ul
li Server: Online
li Database: Online
Trade-offs: Mixins vs. Inheritance
It is tempting to use mixins for everything, but they serve a different purpose than template inheritance (extends and block). Use mixins for small, repeated components (buttons, inputs, cards). Use inheritance for page-level layouts (headers, footers, sidebars).
Over-nesting mixins can make a template difficult to read and debug. If you find yourself nesting mixins three or four levels deep, it is usually a sign that you should be using a layout file instead.
Verification and Implementation
To verify your mixins are working as expected, compile your Pug file using the CLI or the Node.js API. If you reference a mixin that hasn't been defined, Pug will throw a compile-time error, preventing the page from rendering with missing elements.
Practical Check: Run the following command to check your Pug version, as attribute merging behavior can vary between v2 and v3:
# Run in your project terminal
npm list pug
If you are moving mixins into a separate file for a component library, use include components.pug at the top of your main template to make those mixins available.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.