Reusing UI with Pug Mixins: A Practical Guide
Learn how Pug mixins let you build reusable template components, reduce duplication, and keep server‑side rendering tidy. This post walks through a real example, shows how to test it, and discusses when to avoid over‑engineering.
15 Feb 2026, 06:40 UTC

Why Pug Mixins Matter
When you render HTML on the server with Pug, you often end up copying and pasting the same block of markup for buttons, cards, or navigation items. Mixins give you a lightweight way to define a reusable “component” that behaves like a function: you write the markup once, then call it with different arguments to change its appearance or behavior.
Problem Statement
In a growing Node.js application, the team noticed that the same button markup was scattered across dozens of templates. Each copy required manual edits, and a change to the button style had to be propagated everywhere. The goal: centralize the button logic, keep the templates readable, and avoid runtime errors from inconsistent markup.
Thesis
Using Pug mixins, you can encapsulate UI fragments, pass arguments for customization, and include them across templates with a single, well‑tested definition.
Section 1: Defining a Simple Mixin
Mixins are declared with the mixin keyword. Parameters can be required or optional, and you can assign default values. Below is a button mixin that accepts a type, CSS classes, and inner text.
mixin button(type='button', classes='', text='Click')
button(type=type class=classes)= text
Save this in components/button.pug. The mixin body is standard Pug markup; the type=type syntax injects the argument value into the attribute.
Section 2: Invoking the Mixin
To use the mixin, call it with the +button() syntax. The arguments are positional or named. Here’s a template that includes the mixin file and renders three buttons.
include components/button.pug
h1 Button Variants
+button('submit', 'btn btn-primary', 'Save')
+button('reset', 'btn btn-secondary', 'Clear')
+button('button', 'btn btn-link', 'Learn More')
Running the Pug compiler will produce:
<h1>Button Variants</h1>
<button type="submit" class="btn btn-primary">Save</button>
<button type="reset" class="btn btn-secondary">Clear</button>
<button type="button" class="btn btn-link">Learn More</button>
Section 3: Testing the Mixin
Verify that the mixin behaves as expected by compiling the template from the command line. Make sure you have pug installed globally or use npx.
- Open a terminal in the project root.
- Run
npx pug templates/index.pug --out dist(replacetemplates/index.pugwith your actual file path). - Check the output file:
cat dist/index.html. The HTML should match the expected markup shown above. - To test default values, comment out the
typeargument in one invocation:+button(undefined, 'btn btn-warning', 'Alert'). The rendered button should havetype="button"because that is the default.
Risk: If the mixin contains a syntax error, the entire page will fail to render, often resulting in a generic 500 error. Always run the compiler locally before deploying.
Section 4: Trade‑offs and Limitations
- Readability: Complex mixins with many parameters can become hard to read, especially when nested. Keep mixins focused on a single UI element.
- Debugging: Errors inside a mixin surface as generic rendering failures. Adding console logs or using a Pug linter can help pinpoint issues.
- Performance: Mixins are compiled at build time, so there’s no runtime cost beyond the generated HTML. However, excessive nesting may slightly increase compilation time for very large templates.
- Scope: Mixins cannot access server‑side variables unless passed explicitly. If you need dynamic data, pass it as an argument.
Actionable Takeaway
Use mixins for small, repeatable UI fragments that benefit from centralized styling or behavior. Keep the mixin definition simple, test it locally, and include it in a dedicated components directory. Avoid nesting mixins deeper than two levels to preserve readability.
By following this pattern, you’ll reduce duplication, make template updates faster, and maintain a cleaner codebase for server‑side rendered pages.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.