Pug (previously known as Jade) is like HTML on steroids. Once learned, you’ll never miss it. This article isn’t my first post on Pug. I had already written an article about it back in 2013 when it still was called Jade. If you didn’t read it yet, take your time, [go and read through the Pug introduction]({< ref “/an-introduction-to-jade” >}}).

This post covers only a few but powerful features Pug is offering to make you a more productive frontend developer.

Installing Pug using npm

To get the following samples up and running, you’ve to install a Pug compiler. The most comfortable setup uses NPM. The upcoming commands show how to create a new directory and install all required packages that you’ll use throughout this post.

mkdir pug-samples && cd pug-samples
npm init --y
npm install pug --save-dev
npm install live-server jstransformer-markdown jstransformer-uglify-js jstransformer-typescript --save-dev

Next, open the current directory in an editor of your choice and add the change scripts within your package.json file to match the following.

scripts: {
  "build" : "./node_modules/.bin/pug --hierarchy --pretty --out dist .",
  "start": "./node_modules/.bin/live-server dist"
}

You’ve just created two scripts that can be invoked from the terminal using npm. build starts the Pug compiler with some flags to keep HTML files readable, respect the folder structure and put all compiled files into the dist folder. The start script spins up a small HTTP server which you can use for testing.

Blocks

You may have already used blocks in Pug by using the block keyword. However, there is more. You can also decide where your block of code is injected – immediately before or after the block statement – in the parent Pug file. Let’s create a bunch of files that we need for all the samples on blocks.

mkdir blocks
touch blocks/layout.pug
touch blocks/simple-blocks.pug
touch blocks/append-block.pug
touch blocks/prepend-block.pug
touch blocks/append-prepend.pug

First, let’s create a layout. We use this layout for all samples on the blocks feature.

// layout.pug
doctype html
html
  head
    block styles
      link(rel='stylesheet', href='/vendor.css')
  body
    .container
      block content

The template is providing two blocks that can be used within other Pug files to inject some markup. Notice the styles block which is providing some default HTML.

We finished our first implementation by using regular block statements as shown in simple-blocks.pug:

extends ./layout.pug
block styles
  link(rel='stylesheet', href='/portal.css')
block content
  h1 Welcome to our portal

Compile both files using our build script

npm run build

The compiled HTML in dist/blocks/simple-blocks.html looks like this

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="/portal.css">
  </head>
  <body>
    <div class="container">
      <h1>Welcome to our portal</h1>
    </div>
  </body>
</html>

As you can see vendors.css is replaced by the content of the block provided in simple-blocks.pug. This is good for some of the situations, but there are many scenarios where you want different behavior. Adding Stylesheets is just a simple example here. So let’s implement our append-block.pug also, use the append keyword to get both stylesheet references in the HTML.

//append-block.pug
extends ./layout
append styles
  link(rel='stylesheet', href='/portal.css')
block content
  h1 Welcome to our portal

Recompile it using npm run build and you should receive the following HTML in dist/blocks/append-block.html

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="/vendor.css">
    <link rel="stylesheet" href="/portal.css">
  </head>
  <body>
    <div class="container">
      <h1>Welcome to our portal</h1>
    </div>
  </body>
</html>

That’s cool. Besides append there is also prepend, demonstrated in the blocks/prepend-block.pug file.

extends ./layout
prepend styles
    link(rel='stylesheet', href='/init.css')
block content
    h1 Welcome to our portal

As you can imagine, the resulting HTML renders init.css before vendor.css you can also combine both append and prepend to get all three stylesheets in the correct order. See blocks/append-prepend-block.pug for this combination

extends ./layout
prepend styles
  link(rel='stylesheet', href='/init.css')
append styles
  link(rel='stylesheet', href='/portal.css')
block content
  h1 Welcome to our portal

It’s also important to realize that as soon as you use either append or prepend the block becomes optional, and you don’t have to specify block styles explicitly.

Filters

Filters are another great feature of Pug. Because no website is entirely built from Markup, you’ve always to care about other languages. Filters can make this more natural and less error proven. Filters allow you to inject any content — concerning the HTML specs — into your HTML file. To get that working, you’ve to use JavaScript Transformers. Tons of those transformers are available on www.npmjs.org. I’ve chosen three different ones that I want to demonstrate.

Let’s start with an easy one, let’s start with markdown. To compile markdown directly into your HTML, you need jstransformer-markdown that we’ve already installed during the beginning of the article. However, there is no other manual step required here. The Pug compiler uses those transformers automatically.

Create the folder and files we need for our samples.

# move to the project root directory (pug-samples)
mkdir filters
touch filters/markdown.pug
touch filters/uglify-js.pug
touch filters/typescript.pug

Our markdown example is straightforward and goes to filters/markdown.pug:

doctype html
head
  title markdown sample
body
  :markdown
    # Hello from markdown
    This should render as **nice looking HTML**

Again compile the sources using npm run build and take a look at the generated HTML in dist/filters/markdown.html, it should look like this

<!DOCTYPE html>
<head>
  <title>markdown sample</title>
</head>
<body>
  <h1>Hello from markdown</h1>
  <p>This should render as <strong>nice looking HTML</strong></p>
</body>

That’s cool!

We got a clean HTML file. However, there is more. Let’s take a look at jstransformer-uglify-js; it’s responsible for taking regular JavaScript and transforming it into a minified version.

Think about that. We’re invoking pre-processors as we usually do use things like Gulp or Grunt but without the configuration overhead. Everything works by indenting the sources one level underneath the filter :uglify-js.

doctype html
head
  title markdown sample
body
  script
    :uglify-js
      document.addEventListener('DOMContentLoaded', function(){
        console.log('Hello from JavaScript');
      });

becomes after compiling

<!DOCTYPE html>
<head>
  <title>markdown sample</title>
</head>
<body>
  <script>document.addEventListener("DOMContentLoaded",function(){console.log("Hello from JavaScript")});
  </script>
</body>

Moreover, let’s take a look at jstransformer-typescript which is also a mighty pre-processor which calls tsc (TypeScript Compiler) to transpile your TypeScript code directly to ES5 code. The filters/typescript.pug first

doctype html
head
  title markdown sample
body
  button#greet Greet!
  script
    :typescript
      class Greeter{
        greet(name: string){
          alert(`Hello ${name}`);
        }
      }
  
      document.getElementById('greet').addEventListener('click', ()=>{
        var g= new Greeter();
        g.greet('Jade Developer');
      });

Finally, the corresponding HTML from dist/filters/typescript.html

<!DOCTYPE html>
<head>
  <title>markdown sample</title>
</head>
<body>
  <button id="greet">Greet!</button>
  <script>var Greeter = (function () {
    function Greeter() {
    }
    Greeter.prototype.greet = function (name) {
        alert("Hello " + name);
    };
    return Greeter;
    })();

    document.getElementById('greet').addEventListener('click', function () {
      var g = new Greeter();
      g.greet('Jade Developer');
    });
  </script>
</body>

Mixins with Splats

Last but not least, I’d like to show the combination of a mixin with a splat. Splats are well-known language constructs from languages like Ruby or CoffeeScript. It allows you to have a flexible method signature. So your method can receive a flexible range of arguments.

Again let’s create a folder and files first before bringing them to life.

mkdir mixin-with-splats
touch mixin-with-splats/sample.pug

The implementation is quite simple. Look at these few lines of Pug, demonstrating how to define and use such a mixin-splat combination.

mixin nav-list(id, ...items)
  ul.navigation(id=id)
    each item in items
      li.nav-item= item
+nav-list('main-navigation', 'Home', 'Articles', 'Videos', 'Podcasts', 'Forum')

Once compiled, you receive the following HTML.

<ul id="main-navigation" class="navigation">
  <li class="nav-item">Home</li>
  <li class="nav-item">Articles</li>
  <li class="nav-item">Videos</li>
  <li class="nav-item">Podcasts</li>
  <li class="nav-item">Forum</li>
</ul>

The cool thing here is, it doesn’t matter how many arguments you pass to the mixin. It’s entirely up to the scenario.

Summary

As you can see, Pug is a fresh and powerful language that makes you more productive when writing markup. It’s worth looking into it and learning those language features. By combining all those simple things, you could quickly build big projects in almost no time.

All code written here is also available on GitHub.

I hope you enjoyed this tutorial! Leave a comment and share your opinion about Pug or perhaps your experience gathered while using Pug in the wild.