Node.js made its big splash in the web world with the promise of high scalability via asynchrony and the dream of using one scripting language for your web app from front to back. In the past year it found another avenue into the mainstream of the modern web. Developers who never touch Node for the backend are finding great uses for it in their front end workflow. NPM (Node’s packager manager) has become a popular way of distributing components via systems like Bower and Component. Grunt is a micro platform that supports plugins to help you create and organize your distribution files.

Static is the New Black

This shift comes at a time when static sites are back en vogue. When WordPress was first released, I remember thinking how much more “efficient” it seemed to let PHP generate pages on the fly, as opposed to the seemingly-cumbersome process of re-generating HTML files with each change to your blog, the way MovableType did. Today, free or cheap platforms like GitHub Pages and Amazon S3+CloudFront provide a way of hosting static sites that handle huge amounts of traffic with no worries of database throughput, max connections, or backend scripting security flaws.

My favorite combination for creating static sites includes Jade & Stylus. In this post I’ll focus on Jade. I’ll cover the other pieces in a future article.

Jade → HTML

The first time I saw HAML I rolled my eyes. Senstive to indentation and whitespace?! Jade is heavily inspired by HAML, but strips things down even further. I’ve been using it for a couple years now, and I just can’t stand to write HTML any other way. The terse syntax is great, but for static site generation I also make use of templates, includes, and iteration. Jade has some great documentation that includes examples, and you can check the Google Group or Stack Overflow if you get stuck.

Jade Syntax

Jade is like HTML without the HTML; no arrow brackets, no closing tags. id attributes become #, classes just need a ., and a div is assumed unless you specify a different element. So this:

<div class="container">
  <ul id="my-list" class="bucket-list pull-right">
    <li>
      <a href="#">something</a>
    </li>
        
    
    <li>
      <a href="#">something</a>
    </li>
    
  </ul>
  
</div>

Becomes:

.container
  ul#my-list.bucket-list.pull-right
    li
      a(href=“#) something

You can pass “dynamic” data into Jade. Putting a “-” at the beginning of a line tells Jade to evaluate it as Javascript only during compilation; it will not be rendered out in the HTML. Here’s a simple example:

- var pageTitle = Node.js for Static Sites
html
  head
    title= pageTitle

Alteratively, you can use interpolation syntax to render a variable:

- var pageTitle = 'Node.js for Static Sites'
html
  head
    title #{pageTitle}

Iteration

While Jade handles standard Javascript for loops, it also supports a nice shorthand:

- var navElements = ['about', 'contact', 'support']
each el in navElements
  li
    a(href='#')= el

Note that for a few shorthand terms such as each and if you do not need to prepend the line with “-“. Jade can get away with this because there is currently not a “each” element in the HTML spec. 🙂

Layouts

Most of the pages in your site should extend a common layout template. By using the “block” keyword, we can create as many customizable chunks as we need. Blocks can have default content, and the page can either prepend, append, overwrite, or include as-is. This gives us four options and covers almost all your use cases.

For example you might have a “scripts” block in your main layout template that looks something like this:

block scripts
  script(src='/js/jquery.js')

and in one page you need to use some page-specific javascript in addition to the site-wide js files:

append scripts
  script.
    $(function(){ alert hello jade; })

Includes

If you want to include a chunk of Jade inline, it’s as easy as:

include path/to/file

The contents of the included file will get injected in place of that line.

Let’s assume we have a navbar that we want to include in some, but not all, of the pages that extend our main layout. By passing in variables, you could change which nav item is highlighted, for example:

- var activeNav = 'about'
include includes/nav.jade

where nav.jade is:

each el in navElements
  - liClass = (el == activeNav) ? 'active' : ''
  li(class=liClass)
    a(href='#')= el

In line 2 we’re using the ternery operator to set a variable liClass to either ‘active’ or an empty string. When Jade renders that variable into the class of the li, if it finds an empty string it will just drop the class attribute. So we end up with:

<ul>
  <li class="active">
    <a href="#">about</a>
  </li>
      
  
  <li>
    <a href="#">contact</a>
  </li>
      
  
  <li>
    <a href="#">support</a>
  </li>
  
</ul>

Yield

Includes can themselves include other includes, but you might need to go beyond simple variables. You can conditionally inject “dynamic” chunks of HTML in the include. If the potential includes are just one or two different snippets, then you can still use the “includes including includes” method and just use a simple if/else switch. A more flexible option is to use the yield keyword. If I set up a file like this:

include includes/footer.jade
  p here is some content

Then the “p here is some content” will do just what you expect; get nested inline with the include, below it. If instead we use a yield statement inside the include file, then anything that we nest under the include statement will get injected there. So using the example above, I could make that “p here is some content” show up in the middle of other elements in the footer by doing something like this in includes/footer.jade:

.footer
  p this site is brought to you by StrongLoop
  yield
  p all rights reserved 2013

Grunt the Jade

In case you’re not already familiar, here’s a great introduction to Grunt. Once your main file is set up, you can add the Jade-specific bits:

jade: {
  compile: {
    options: {
      pretty: true
    },
    files: [
      {expand: true, cwd: 'src/jade', src: '*.jade', dest: '<%=meta.endpoint%>/', ext: '.html'}
    ]
  }
},

If you want the HTML files to be generated automatically after every time you save a .jade file, we can finger our Grunt file’s ‘watch’ section to include this:

watch: {
  html: {
    files: '**/*.jade',
    tasks: ['jade'],
    options: {
      interrupt: true
    }
  },

Then just run ‘grunt watch’ from your command line.

Other Tips & Resources

  • Try out Jade’s syntax with an interactive sandbox
  • Convert HTML to Jade
  • Pass a variable from a child template to its parent layout by putting a ‘pageVars’ block at the top of the layout, and add the content in your child/include.
  • Compile Jade templates into JS snippets that can be used for client-side rendering in a browser, as an alternative to something like Handlebars.
  • Render Jade objects into inline JS with:
    script.
      var slides = !{JSON.stringify(ss)};
    

Putting it all Together

It can take some trial and error to get it all straight; templates, includes, blocks, and yields. Just as with HTML and Javascript, there are several ways to achieve any given desired output. Once you get the hang of it, you’ll find your own preferences and be comfortable refactoring when you realize what you’ve been including should be part of a layout.

Use StrongOps to Monitor Node Apps

Ready to start monitoring event loops, manage Node clusters and chase down memory leaks? We’ve made it easy to get started with StrongOps either locally or on your favorite cloud, with a simple npm install.

Screen Shot 2014-02-03 at 3.25.40 AM

What’s next?

  • What’s in the upcoming Node v0.12 release? Big performance optimizations, read Ben Noordhuis’ blog to learn more.
  • Watch Bert Belder’s comprehensive video presentation on all the new upcoming features in v0.12
  • Ready to develop APIs in Node.js and get them connected to your data? We’ve made it easy to get started either locally or on your favorite cloud, with a simple npm install.