Register to get access to free programming courses with interactive exercises

Mixins HTML: Preprocessor Pug

There's quite a large number of similarly structured blocks when designing a site. Imagine a catalog for an online store. It can consist of hundreds of cards, which differ only in terms of their data. When developing the old-fashioned way, without templating, all these cards will be made using the “copy-paste” method. It's not perfect, but it works. That's what it feels like until the cards change. Any addition/removal of elements will result in hours or even days of work to change the layout.

It seems unbelievable, but these sorts of situations aren't as rare as they may seem. Therefore, it's important for developers to learn how to work with repeating portions of code.

Pug uses mixins, a construct that allows you to define and insert code in any part of a layout, in order to reuse the layout. This is a great solution for creating components.

We'll use a button component as an example. This is an uncomplicated construct that can consist of just one tag. We use the keyword mixin, naturally, to create a mixin, followed by whatever name you like for the mixin itself. While it's up to you to choose the name, it's important to give it a good name. All the HTML we need is stored inside the mixin. In this case, it's one <button> tag:

mixin buttonOrder
  button.btn.btn-order Place an order

That's it. A simple design, but with a lot of features, which we'll talk about a little later. Now you need to call this mixin to get its body into the resulting HTML code. Here, it's important to understand that the mixin itself doesn't get into the final markup in any way. To call a mixin, we use the + symbol and the name of the mixin. After that, it'll be inserted into the HTML code.

mixin buttonOrder
  button.btn.btn-order Place an order

+buttonOrder
<button class="btn btn-order">Place an order</button>

Each mixin can be called an unlimited number of times:

mixin buttonOrder
  button.btn.btn-order Place an order

+buttonOrder
+buttonOrder
+buttonOrder
<button class="btn btn-order">Place an order</button>
<button class="btn btn-order">Place an order</button>
<button class="btn btn-order">Place an order</button>

If mixins just add existing markup, wouldn't it make more sense to put it in a separate file and include it? There's an ongoing debate about this, but it's worth keeping the semantics of these constructs in mind: mixins are needed to isolate a certain piece of wrapped layout so it can be reused quickly. Connecting a file is required for adding large logical constructs, such as templates, scripts, objects, or arrays.

If you need a wrapper for a logical component, you use a mixin, and if you want to include a template in a specific Pug file, you use the include construct.

Mixins as functions

In Pug, mixins act not only as HTML substitutes, but also as functions that return HTML. The distinguishing feature of these mixins is that there are certain arguments used when calling them. Let's go back to the button system from the last example. There are often several different kinds of buttons on app pages. They may differ in terms of text, classes, and so on.

You can create the necessary number of mixins, but if the elements are subject to a single design system, it's not the most convenient option because if you need to change the overall style of the buttons, you'll need to edit each mixin.

Let's add the ability to pass some text to the mixin with the button. To do this, when we define a mixin, we put the arguments we need in brackets The name of the argument is completely up to you. Arguments inside mixins are interpreted as an ordinary variable, so we work with it in a fairly standard way.

mixin button(title)
  button.btn.btn-order= title

Here's how you call mixins:

mixin button(title)
  button.btn.btn-order= title

+button('Order')
<button class="btn btn-order">Order</button>

Mixins can also take default values. This is useful when most elements have the same content. It's sufficient to assign the value you need in the mixin definition:

mixin button(title = 'Order')
  button.btn.btn-order= title

+button
+button('Place an orderч')
<button class="btn btn-order">Order</button>
<button class="btn btn-order">Place an orderч</button>

If the default value of the argument is used, there's no need to add empty parentheses when calling, unlike Sass.

Mixins are also convenient to use for templating, combining the features learned earlier. For example, outputting of users in a table. If there are several different user tables on the site, the most convenient option is to create a mixin and pass the necessary user data into it.

-
  const users = [
    {
      name: 'Hexlet',
      surname: 'McCoderson',
      login: 'hexlet-code',
      score: 1271
    },
    {
      name: 'Layout',
      surname: 'ODesign',
      login: 'king-of-layout',
      score: 1100
    },
  ]

mixin createUserTable(usersData = [])
  table
    thead
      tr
        th Name
        th Surname
        th Login
        th Score
    tbody
      each user in usersData
        tr
          td= user.name
          td= user.surname
          td= user.login
          td= user.score
      else
        tr
          td(colspan='4') No users

section
  h2 The best of the best
  +createUserTable(users)

  h2 The worst of the worst
  +createUserTable(badUsers)

Once you've created a mixin, you can use it with all sorts of different data that comes to Pug. You can go even further and check fields and value types, and validate and normalize data. We've given an empty array as the default value. This ensures that if data is missing or the variable doesn't exist, an empty array will be inserted instead.


<section>
  <h2>The best of the best</h2>
  <table>
    <thead>
      <tr>
        <th>Name</th>
        <th>Surname</th>
        <th>Login</th>
        <th>Score</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Hexlet</td>
        <td>McCoderson</td>
        <td>hexlet-code</td>
        <td>1271</td>
      </tr>
      <tr>
        <td>Layout</td>
        <td>ODesign</td>
        <td>king-of-layout</td>
        <td>1100</td>
      </tr>
    </tbody>
  </table>

  <h2>The worst of the worst</h2>
  <table>
    <thead>
      <tr>
        <th>Name</th>
        <th>Surname</th>
        <th>Login</th>
        <th>Score</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td colspan="4">No users</td>
      </tr>
    </tbody>
  </table>
</section>

Passing nested content to the mixin

Mixins can be used as a skeleton of the structure of a component. This can be the design of an article that'll be displayed on a page in the form of the following template:

<article class="post">
  <h2>Article title</h2>

  <div class="post-body">
    <p>The text of the article</p>
  </div>

  <div class="post-author">
    <p>Author of the article</p>
  </div>
</article>

If there are lots of these sorts of articles on the site, it makes sense to make a template in a separate mixin. But how do you transfer the data? There are two ways:

  1. Define the arguments, and pass them to the mixin separately.
  2. Somehow pass the whole article at once.

The first method may be inconvenient because of the body of the article. After all, an article is not a one-sentence work that can be conveyed as a mixin argument. In addition, articles are often stored in a separate file, as was the case in the lesson about include. Therefore, the best option in this situation might be to pass the text directly to the mixin using nesting. Let's call the article mixin by passing the title and author of the article as arguments, and putting the inclusion of the file in the body.

+article('Article title', 'Article author')
  include:markdown-it main.md

The main question is where the result of the include:markdown-it main.md will be placed. Actually, it won't go anywhere! The mixin doesn't know what to do with this data, even though it can accept it. To insert the nested data in the mixin, we the block construct, which stores what has been nested in the mixin. You can think of it as an “invisible argument,” which is always there but not explicitly declared. Now you can implement the logic of the mixin:

mixin article(name, author)
  .post
    h2= name

    .post-body
      block

    .post-author= author

+article('Article title', 'Article author')
  include:markdown-it main.md
<div class="post">
  <h2>Article title</h2>
  <div class="post-body">
    <p>The article text from the main.md file</p>
  </div>
  <div class="post-author">Article author</div>
</div>

Additional assignment

Complete the mixin with the addition of an article to the page:

  1. Check that there's content to display. If there isn't, print an error.
  2. Add default values for the arguments. Use “Hexlet” as the article name. And "McCoderson" as the author

Hexlet Experts

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

About Hexlet learning process

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.bookmate">Bookmate</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.healthsamurai">Healthsamurai</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.dualboot">Dualboot</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.abbyy">Abbyy</span>
Suggested learning programs
Layout Designer icon
Profession
Under development beginner
Layout with the latest CSS standards
start anytime 5 months

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.