Using the Pug Templating Engine Part 4 - Includes & Mixins
Now that we've covered templating, logic, and layouts, it's time to cover the last major piece of Pug: includes and mixins. These concepts also fall into the DRY category and can help make your life as a developer much easier. First up? Includes.
How to use includes in Pug
Imagine you've got a piece of your site that will be used in various places on your site, such as a contact form. If you wanted that on two different pages, you'd have to create the form manually on each page even though they're exactly the same. Again, that leads to issues with having to update something in multiple locations. Who wants to do that?
This is exactly the reason Pug gives us the ability to use includes. Includes allow you to write some static code (no dynamic data) and put it wherever you want in the templates. In our form example, the form code would just be standard Pug templating. The code below shows what's in my form template.
// includes/form.pug form(action='/action-goes-here' method='post') label(for='name') Name: input(type='text' name='name' id='name') label(for='message') Message: textarea(name='message' id='message') input(type='submit' value='Submit')
I have created a simple form with a name input, a message input, and a submit button. Rather than writing that out (or copying and pasting) everywhere I want it, I can include it. I will be using the page examples from my previous post to illustrate how things fit together. Below is an example showing how to include the form in my
// hello.pug extends layout.pug block content h1 Hello, world! include includes/form.pug block scripts script console.log('Hello, world!')
h1 tag, I have added a line with the
include keyword followed by the path to the file I want to include in the markup. Now the markup for that page will look like this:
<!-- output --> <html> <head> <title>My Site</title> </head> <body> <header> <a href="/">Home</a> </header> <h1>Hello, world!</h1> <form action="/action-goes-here" method="post"> <label for="name">Name:</label> <input type="text" name="name" id="name" /> <textarea name="message" id="message"></textarea> <input type="submit" value="Submit" /> </form> <footer> <p>Site Footer</p> </footer> <script> console.log('Hello, world!'); </script> </body> </html>
Honestly, that's really all there is to know about includes. You write code, then include it somewhere. It is worth noting that using when using includes, the whole file is included. There's not any way to only pull certain pieces out for reuse. This is also the way you pull other data such as mixins into the current file. What if you needed this sort of idea, but with dynamic data? That's where mixins come into play.
What are mixins used for in Pug?
Mixins allow you to write chunks of Pug markup with or without dynamic data, then use that markup anywhere you want. This allows you to write a sort of "mold" of a component, then fill it out with relevant data later on. A common example of this concept is cards on social media sites. Every post gets the same basic card to put the content and user info into, but it's just filling the component with different information each time instead of being written out every time it appears.
So how do you create a mixin? Below is an example of a mixin to display a post of some sort.
// mixins/post.pug mixin post(title, content) article h2= title p= content
Voila! Just like that, you have a post component. When the mixin is called, a title and content argument can be passed in and displayed appropriately. Pretty cool, huh? Now that the post mixin is ready, let's use it in a page.
// hello.pug extends layout.pug include mixins/post.pug block content h1 Hello, world! include includes/form.pug +post('This is the title', 'This is the content') block scripts script console.log('Hello, world!')
hello.pug page looks pretty much the same as before, but with a few changes. First, there is an include statement beneath the
extends layout.pug line. This is how we include the file which holds the mixin. Because it's a mixin and not just markup, it's not going to actually display anything where the include is placed. Instead, you need to call the mixin with a plus sign.
As you can see, the example above calls the post mixin,
After using the mixin in a template, the compiled markdown will look like this:
<!-- output --> <html> <head> <title>My Site</title> </head> <body> <header> <a href="/">Home</a> </header> <h1>Hello, world!</h1> <form action="/action-goes-here" method="post"> <label for="name">Name:</label> <input type="text" name="name" id="name" /> <textarea name="message" id="message"></textarea> <input type="submit" value="Submit" /> </form> <article> <h2>This is the title</h2> <p>This is the content</p> </article> <footer> <p>Site Footer</p> </footer> <script> console.log('Hello, world!'); </script> </body> </html>
It's not hard to see how this capability can make development go much faster. Passing data between code chunks or iterating over posts to spit out data programmatically is much easier when it can be done this way. Overall, you should have all of the skills needed to build most things using Pug as a templating engine, but I would like to talk about one more thing before I wrap up this post.
All of the concepts we've discussed can be used together. For instance, you could create a block inside of an include. You could put a block inside of a mixin. You could even put a block inside of a mixin inside of an include which is in another mixin. The possibilities are endless, though the deeper you go, the harder it is to maintain the project.
I just wanted to illustrate just how flexible Pug can be to meet just about any need you may have when creating your templates.
Pug is an incredibly versatile templating engine for Nodejs applications. I have covered just about everything you need to build dynamic websites or applications as easily as you can. Maybe your next project will have a Pug frontend, maybe not. Either way, you know more than you did before (hopefully) and that's always a good thing.
Have questions? You can find me on Twitter @iam_timsmith.