An Introduction to the Pug Template Engine for Writing HTML

Joshua Hall

HTML was, for most of us, our first step into the world of web development. At a simpler time when a creating a website was like nothing more than some structural tags with a bit of styling.

But as we grow, so do our tools. CSS, with its brackets, semicolons, and potential specificity traps, eventually paved the way for tools like Sass, which allowed us to clean up our files, write faster, and gave us a remarkable array of possibilities.

Now, in the modern age of compilers, HTML has been given the same opportunity for redemption. No longer do we need to worry about closing tags or copy/pasting repeated markup.

Something as common as an unorganized list (ul) can be created with two lines regardless of the amount of items in it. You can even build custom components that conditionally render markup based on the inputs. This is made possible thanks so a template engine for Node.js and the browser called Pug (formerly known as Jade).

In case you’re feeling like diving head first into Pug and its features, here are the official docs.

Getting Started with Pug

Pug doesn’t need to be installed, any file ending in .pug will be recognized in code editors like VSCode or Atom. But the browser obviously won’t know what to do with it, so for the sake of simplicity I recommend downloading Prepros. It’s completely free but it’ll pop up a little screen begging for attention sometimes, ¯\_(ツ)_/¯.

Or if you’re less lazy than me you can use the babel plugin.

Just create and index.pug file and add whatever folder it’s in to prepros.

If you look at the file in Prepros, you'll see some options for Pretty and Auto Compile, make sure Auto Compile is activated.

Our boilerplate is going to look a little different from normal HTML, if you’re using VSCode or have emmet installed just use ! then tab.

index.pug

<!DOCTYPE html>
html(lang="en")
  head
    meta(charset="UTF-8")
    meta(name="viewport", content="width=device-width, initial-scale=1.0")
    meta(http-equiv="X-UA-Compatible", content="ie=edge")
    link(rel="stylesheet", href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css")
    link(rel="stylesheet", href="style.css")
    title Intro to Pug
  body

Indentation

As you can see, instead of having <head></head> we can just have the name and everything down and right of it will be included between the tags after it’s compiled.

Adding something to the body is just as easy, and to stop adding to an element just make the next one indented evenly with it.

The following…

body 
  div 
    h1 Hello
  h1 World

Renders to:

<body>
  <div> 
    <h1>Hello</h1>
  </div>
  <h1>World</h1>
</body>

Inline Elements

While this is clearly easier to write and much nicer to look at it would be disadvantageous in some situations. What if you want a <button> tag inside of a <a> tag? It would be a waste of space to have to break the button onto it’s own line,and what if you need an <img> inside of it? Three lines for one thing wouldn’t be very nice.

Luckily, we have a way around that by using : on the end of the parent element and the next item can remain inline and work the same.

a(href=""): button: img(src="", alt="")

Outputs to:

<a href="">
  <button><img src="" alt=""/></button>
</a>

Classes and Id’s

Any attribute will work almost the same as before, just inside of parentheses instead.

As a shorthand, you can give classes and ids by adding a period . or hash symbol # to the end of the element followed by the name you wish to give it. The order doesn’t matter and you can add as many as you want.

h1.title#content.important

Becomes:

<h1 class="title important" id="content"></h1>

Loops

Remember when I said you can make an infinite list with two lines? Using each we can make a basic array and generate whatever we want from each item.

index.pug

ul: each val in ["home", "blog", "about", "contact"]
  li=val

//- Or you can get the index and use interpolation with the value
ul: each val, index in ["home", "blog", "about", "contact"]
  li=index+1 + ": " + val

//- Or, better yet, use pug's interpolation method of putting your variables in #{}
ul: each val, index in ["home", "blog", "about", "contact"]
  li #{index + 1}: #{val}
<ul>
  <li>home</li>
  <li>blog</li>
  <li>about</li>
  <li>contact</li>
</ul>

<ul>
  <li>1: home</li>
  <li>2: blog</li>
  <li>3: about</li>
  <li>4: contact</li>
</ul>

<ul>
  <li>1: home</li>
  <li>2: blog</li>
  <li>3: about</li>
  <li>4: contact</li>
</ul>

Mixins and Includes

Just as Sass allows us to create mixins for reusable blocks of code, Pug allows us to do the same. Here’s an example I use very often.

Let’s put this into a separate file and import it into index.pug. Adding a hash # to the file name tells the compiler to just import the contents without converting the file itself.

/mixins/#card.pug

//- Initiate a card with all its arguments
mixin card($icon, $title, $info, $classes)
  .card(class=$classes)
    img(src=$icon)
    h3=$title
    p=$info

Now let’s import and use it:

index.pug

body 
  include /mixins/#card //-You can ommit the .pug ending

  +card('', 'Dwarf Crocodile', 'Osteolaemus tetraspis')
  +card('', 'Black caiman', 'Melanosuchus niger')
  +card('', 'Alligator Gar', 'Atractosteus spatula')

  //- Or we can get even crazier and can use an each loop and pass in an array of objects. Although it takes up more space and is basically JavaScript at this point.
  each val in [
    {name: 'Dwarf Crocodile', SciName: 'Osteolaemus tetraspis'}, 
    {name: 'Black caiman', SciName: 'Melanosuchus niger'},
    {name: 'Alligator Gar', SciName: 'Atractosteus spatula'}]
      +card('', val.name, val.SciName)

Conditionals

Just like in JavaScript, we’re allowed to use if, else if, and else and even all of the same logical operators like !, &&, and ||.

We can make our mixin a bit more efficient so it’s not rendering empty img tags.

mixin card($icon, $title, $info, $classes)
  .card(class=$classes)
    if $icon
      img(src=$icon)
    else  //Use a placeholder img if one is not given
      img(src='./imgs/placeholder.jpg')
    h3=$title
    if $info 
      p=$info
    else 
      p Nothing Found

Pug, I think, I one of the most underappreciated template languages out there, especially when you look at the success of Sass which is focused so much around the same goals. I hope that after reading through this rough overview you can begin see the possibilities of the this wonderful technology.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#