While I was working on the 24 CSS artworks for my CSS Christmas Calendar I quickly realized that I would have to write <div>
and </div>
a million times.
Besides being very monotone, it is also extremely error-prone. It's so easy to forget closing a div
, or other elements.
I wanted to save myself from a lot of unnecessary keystrokes and headaches.
That's why I decided to give Pug - a HTML templating engine -
a try.
What Is Pug?
Pug.js is a HTML templating engine, which translates pug code to HTML tags.
It requires some configuration in your backend, so it gets compiled. But luckily, it's easy to get started on Codepen without any local setup.
Just create a new pen, go to the settings, and enable the Pug templating engine.
So how to write pug templates?
Here's a simple example:
that compiles to
<div class="hot-chocolate">
<div class="mug">
<div class="mug__grip"></div>
<div class="mug__contents"></div>
</div>
</div>
Let's highlight some important things here:
-
indentation is everything! if you start the next line under the previous, it will be a sibling HTML element. But if you press a
TAB
, it will be the children of it. - no need for closing tags. The proper indentation will take care of them automatically.
- use your existing CSS knowledge: if something starts with a
.
, it will be a class.#
stands for id.
Easy Peasy. Something More Complex?
So far we only worked with empty div
s.
But you can add content to them:
.title Santa Claus
.description.
a plump, white-bearded, red-suited, and jolly old man
in modern folklore who delivers presents to
children at Christmastime
As you could see, multiline content needs to be indented one level, and the element definition needs a .
at the end.
Changing the tag from div
is just as simple:
a.link I'm a link
span.highlight I'm a highlighted text
footer I don't even have a class
Finally, you can edit the elements attributes the following way:
input(type='text' name='address' autofocus)
a(href='https://csschristmascalendar.com' target='_blank')
Mixing Pug with JavaScript
As I mentioned, Pug is a templating engine written in JavaScript.
Therefore, you can combine it with any Javascript code, and that's where it's superpower begins.
- let authenticated = false;
.login(class=authenticated ? 'hidden' : 'visible')
Here the class changed based on a JavaScript variable. Notice, how class
is used as an attribute, and not in the selector.
Also, the plain JavaScript line starts with a dash -
to mark that it's not a template
- let length = 10;
ul.items(style=`height: ${Math.min(length * 2, 10)}rem`)
Here the height will be a maximum of 10rem.
Finally, one thing I found quite useful is combining pug with the random()
function:
.star(style=`transform: rotate(${Math.random() * 360}deg)`)
This will set a random rotate transform on the div
.
Conditional Rendering
One of the cool things in Pug is that it can conditionally render elements.
Here's an example:
- let authenticated = false;
.header
if (authenticated)
a(href='/logout') Sign out
else
a(href='/login') Sign in
This example will render either the Sign in or Sign out button, depending on the authenticated variable.
Loops That Will Save Your Time
If you have to render the same block of code multiple times, loops will save you from unnecessary repetition.
Let's start with a simple case, looping through an array of items:
- const menuItems = ['intro', 'about', 'portfolio', 'contact'];
ul
each menuItem in menuItems
li
a(href=`#${menuItem}`)= menuItem
This will compile to:
<ul>
<li><a href="#intro">intro</a></li>
<li><a href="#about">about</a></li>
<li><a href="#portfolio">portfolio</a></li>
<li><a href="#contact">contact</a></li>
</ul>
Notice, how I added an equal character =
after the <a>
tag. That to tell Pug that what comes after is a JavaScript variable, so it doesn't print the word menuItem for each link.
For loop is another one that I used heavily in the CSS arts. It repeats the same content n times.
It's important to note that for is not a Pug technique, but plain JavaScript. However, that's the beauty of it all: you can combine the two, without any problems:
.sky
- for (var i = 1; i <= 10; i++)
.star
This will create 10 <div class="star"></div>
elements inside the sky
I used this trick in almost all items, often combined with more complex parameters:
This creates the following HTML
<div class="sparkler-light">
<div class="spark spark-1" style="--spark-rotate: 10deg;
--spark-delay: 359ms"></div>
<div class="spark spark-2" style="--spark-rotate: 20deg;
--spark-delay: 887ms"></div>
...
<div class="spark spark-36" style="--spark-rotate: 360deg;
--spark-delay: 121ms"></div>
</div>
With the for loop I could add 36 sparks rotated by 10 degrees each, and with a random delay in their animation, that - combined with CSS - resulted in this:
Sounds Cool! What's Next?
Now you are ready to enable your Pug templating engine!
You can make more use of it by including other pug files, creating complex layouts, or even including markdown files.
Adding Pug to your projects is very easy, just follow the official documentation. It has very straightforward steps about the setup and integration.
If you just want to manually convert a Pug template to HTML, I found this online converter that did an amazing job for me, and it will certainly make it for you too.
Finally, if you need a quick overview of the most common Pug syntaxes, check this cheat sheet.
You can come back to it anytime you need a specific syntax, especially for the more complex elements.
I'd love to hear from you, and see how you implemented Pug in your projects.
Leave a comment below, and I'd be glad to answer your questions and comments.
Top comments (2)
I just created my first project using pug (as view-renderer in express) last week. And I think u hit the nail on the head in your introduction. Writing html can be tiring and also is confusing / uneasy to read.
Pug is really clear and is written super fast. Initially I chose it for reusing templates in other templates (like header and footer), but I fell in love immediatly. <3
I still need to check the cheat sheet every now and again, but I already feel like I absorbed everything necassary to build the templates I want.
Yes, I felt the same.
The learning curve is really quick for basic use cases, and I could just quickly check the cheat sheet whenever I needed something more complex.
I don't know how old this whole Pug.js is, or how widespread (honestly I didn't hear about it until a month ago) but it's really efficient so people should go and use it