When I started working at Storify as a Node.js Engineer. The tech stack was Express and Jade. I hate to admit it, but I struggled with Jade a lot!
Before, I mostly worked with Underscore, and Handlebars. I attempted to modify some HTML in the Jade templates. Other times I would only change the text. Those were trivial updates, but very often they cause the whole server to crash.
I was failing miserably to learn by trial and error. I hated Jade. I was starting to hate editing templates as well. Then I had a light bulb moment: I need a tutorial. I went to the official docs. I wish this article existed at that time. After spending just an hour learning Jade, I was able to use Jade and make all the changes to templates smoothly.
Smart people learn by their mistakes, and wise people learn from others. Don’t repeat my folly. Skim through this Jade tutorial to harness the power of this wonderful template language.
A template engine is a library or a framework that uses some rules/languages to interpret data and render views. In the case of web applications, views are HTML pages (or parts of them), but they can be JSON or XML files, or, in desktop programs, GUIs. For those of you familiar with the model–view–controller concept, templates belong to the view.
In web apps, it’s beneficial to use templates because we can generate an infinite number of pages dynamically with a single template! Another side benefit is when we need to change something; we can do it in one place only.
If we go back to the diagrams in the previous chapter (traditional vs. REST API approaches), we can deduce that templates can be compiled into HTML either server-side (traditional approach) or client-side (REST API approach). No matter which approach we take, the syntax of the libraries themselves remains intact.
In this article, I’ll cover the following:
- Jade syntax and features
- Jade standalone usage
Jade Syntax and Features
Jade is a Node.js brother of Haml, in the sense that it uses whitespace and indentation as part of its language. Therefore, we need to be careful to follow the proper syntax.
You can follow the Jade syntax examples in this section, online, at the official web site’s demo page (http://jade-lang.com/demo) or at the @naltatis resource (http://naltatis.github.io/jade-syntax-docs/), or by writing standalone Node.js scripts (examples are presented in “Jade Standalone Usage,” which appears later in this chapter).
Tags
Any text at the beginning of a line—by default—is interpreted as an HTML tag. The main advantage of Jade is that this text renders both closing and opening tags for the HTML element, as well as the <></>
symbols. Therefore, we save many keystrokes as developers writing in Jade!
The text following a tag and a space (e.g.,tag <text>
) is parsed as the inner HTML (i.e., content inside the element). For example, if we have the following Jade code:
Body
div
h1 Practical Node.js
p The only book most people will ever need.
div
footer © Apress
The output of the template above will be:
<body>
<div>
<h1>Practical Node.js</h1>
<p>The only book most people will ever need.</p>
</div>
<div>
<footer>© Apress</footer>
</div>
</body>
Variables/Locals
Data that are passed to the Jade template are called locals. To output the value of a variable, use =
. See the following examples:
Jade code:
h1= title
p= body
Locals:
{
title: "Express.js Guide",
body: "The Comprehensive Book on Express.js"
}
HTML output:
<h1>Express.js Guide</h1>
<p>The Comprehensive Book on Express.js</p>
Attributes
Attributes are added by putting them into parentheses right after the tag name. They follow name=value
format. In addition, multiple attributes need to be separated by a comma. For example,
div(id="content", class="main")
a(href="http://expressjsguide.com", title="Express.js Guide", target="_blank") Express.js Guide
form(action="/login")
button(type="submit, value="save")
div(class="hero-unit") Lean Node.js!
turns into:
<div id="content" class="main"><a href="http://expressjsguide.com" title="Express.js Guide"
target="_blank">Express.js Guide</a>
<form action="/login">
<button type="submit" value="save"></button>
</form>
<div class="hero-unit">Learn Node.js</div>
</div>
Sometimes, the value of an attribute needs to be dynamic. In this case, just use the variable name! The pipe, or |
, allows us to write the content of the HTML node on the new line—in other words, the line with the pipe becomes inner text an example is as follows:
a(href=url, data-active=isActive)
label
input(type="checkbox", checked=isChecked)
| yes / no
The template above is provided with locals:
{
url: "/logout",
isActive: true,
isChecked: false
}
And they both, i.e., template and locals data, produce output:
<a href="/logout" data-active="data-active"></a>
<label>
<input type="checkbox"/>yes / no
</label>
Note that the attribute with the value false
is omitted from the HTML output. However, when no value is passed, true
is assumed—for example:
input(type='radio', checked)
input(type='radio', checked=true)
input(type='radio', checked=false)
<input type="radio" checked="checked"/>
<input type="radio" checked="checked"/>
<input type="radio"/>
Literals
For convenience, we can write classes and IDs right after tag names. For example, we can then apply lead
and center
classes to a paragraph, and create a div
element with the side-bar
ID and pull-right
class (again, the pipe signifies an inner text):
div#content
p.lead.center
| webapplog: where code lives
#side-bar.pull-right
span.contact.span4
a(href="/contact") contact us
<div id="content">
<p class="lead center">
webapplog: where code lives
<div id="side-bar" class="pull-right"></div>
<span class="contact span4">
<a href="/contact">contact us</a>
</span>
</p>
</div>
Note that if the tag name is omitted, div
is used instead.
Text
Outputting raw text is done via |—for example:
div
| Jade is a template engine.
| It can be used in Node.js and in the browser JavaScript.
Script and Style Blocks
Sometimes, developers want to write chunks of content for script
or style
tags in the HTML! This is possible with a dot. For example, we can write inline front-end JavaScript like this:
script.
console.log('Hello Jade!')
setTimeout(function(){
window.location.href='http://rpjs.co'
},200))
console.log('Good bye!')
<script>
console.log('Hello Jade!')
setTimeout(function(){
window.location.href='http://rpjs.co'
},200))
console.log('Good bye!')
</script>
JavaScript Code
Contrary to the previous example, if we want to use any JavaScript at template compilation time—in other words, to write executable JavaScript code that manipulates the output of the Jade (i.e., HTML)—we can use the -
, =
, or !=
symbols. This might come in handy when we output HTML elements and inject JavaScript. Obviously, these types of things should be done carefully to avoid cross-site scripting (XSS) attacks. For example, if we want to define an array and output <>
symbols, we can use !=
.
- var arr = ['<a>','<b>','<c>']
ul
- for (var i = 0; i< arr.length; i++)
li
span= i
span!="unescaped: " + arr[i] + " vs. "
span= "escaped: " + arr[i]
produces this:
<ul>
<li><span>0</span><span>unescaped: <a> vs. </span><span>escaped: <a></span></li>
<li><span>1</span><span>unescaped: <b> vs. </span><span>escaped: <b></span></li>
<li><span>2</span><span>unescaped: <c> vs. </span><span>escaped: <c></span></li>
</ul>
■ T i p One of the main differences between Jade and Handlebars is that the former allows pretty much any JavaScript in its code whereas the latter restricts programmers to only a handful of built-in and custom-registered helpers.
Comments
When it comes to comments, we have a choice to output them or not. For the former, use JavaScript style //;
for the latter, use //-
. For example,
// content goes here
p Node.js is a non-blocking I/O for scalable apps.
//- @todo change this to a class
p(id="footer") Copyright 2014 Azat
outputs:
<!-- content goes here-->
<p>Node.js is a non-blocking I/O for scalable apps.</p>
<p id="footer">Copyright 2014 Azat</p>
Conditions (if )
Interestingly enough, in addition to the standard JavaScript code where the if
statement can be used by prefixing it with -
, we can use a minimalistic Jade alternative with no prefix and no parentheses—for example:
- var user = {}
- user.admin = Math.random()>0.5
if user.admin
button(class="launch") Launch Spacecraft
else
button(class="login") Log in
There’s also unless, which is equivalent to not
or !
.
Iterations (each loops)
Similar to conditions, iterators in Jade can be written simply with each—
for example:
- var languages = ['php', 'node', 'ruby']
div
each value, index in languages
p= index + ". " + value
The HTML output is as follows:
<div>
<p>0. php</p>
<p>1. node</p>
<p>2. ruby</p>
</div>
The same construction works with objects as well:
- var languages = {'php': -1, 'node': 2, 'ruby':1}
div
each value, key in languages
p= key + ": " + value
The Jade above is compiled into the HTML output:
<div>
<p>php: -1</p>
<p>node: 2</p>
<p>ruby: 1</p>
</div>
Filters
Filters are used when there are blocks of texts written in a different language. For example, the filter for Markdown looks like this:
p
:markdown
# Practical Node.js
This book(http://expressjsguide.com), really helps to grasp many components needed for modern-day web development.
■ Note The Markdown modules still need to be installed. The marked
and markdown NPM packages are often used for this. There’s no need for an additional configuration, just install them in the project’s local node_modules
folder.
Interpolation
Interpolation in Jade is achieved via #{name}
. For example, to output title
in a paragraph, do the following:
- var title = "Express.js Guide"
p Read the #{title} in PDF, MOBI and EPUB
The interpolation is processed at template compilation; therefore, don’t use it in executable JavaScript (-
).
Case
Here’s an example of the case
statement in Jade:
- var coins = Math.round(Math.random()*10)
case coins
when 0
p You have no money
when 1
p You have a coin
default
p You have #{coins} coins!
Mixins
Mixins are functions that take parameters and produce some HTML. The declaration syntax is mixin name(param,param2,...)
, and the usage is +name(data)
. For example:
mixin row(items)
tr
each item, index in items
td= item
mixin table(tableData)
table
each row, index in tableData
+row(row)
- var node = [{name: "express"}, {name: "hapi"}, {name: "derby"}]
+table(node)
- var js = [{name: "backbone"}, {name: "angular"}, {name: "ember"}]
+table(js)
The template and data above produce this HTML:
<table>
<tr>
<td>express</td>
</tr>
<tr>
<td>hapi</td>
</tr>
<tr>
<td>derby</td>
</tr>
</table>
<table>
<tr>
<td>backbone</td>
</tr>
<tr>
<td>angular</td>
</tr>
<tr>
<td>ember</td>
</tr>
</table>
Include
include
is a way to split logic into a separate file for the purpose of reusing it across multiple files. It’s a top-to-bottom approach; we dictate what to use in the file that includes another file. The file that includes is processed first (we can define locals there), then the included file is processed(we can use earlier defined locals).
To include a Jade template, use include /path/filename. For example, in file A:
include ./includes/header
Notice there’s no need for double or single quotes for the template name and its path. It’s possible to traverse up the tree:
include ../includes/footer
But, there’s no way to use a dynamic value for the file and path (use a variable), because includes/partials are handled at compilation (not at runtime).
Extend
extend
is a bottom-to-top approach (as oppose to include
), in the sense that the included file commands which parts of the main file it wants to replace. The way it works is with extend filename
and block blockname
statements:
In file_a
:
block header
p some default text
block content
p Loading ...
block footer
p copyright
In file_b
:
extend file_a
block header
p very specific text
block content
.main-content
Standalone Jade Usage
Template engines are not always used with Node.js (and frameworks like Express.js). Sometimes, we might just want to use Jade in a standalone manner. The use cases include generating an e-mail template, precompiling Jade before deployment, and debugging. In this section, we do the following:
- Install a Jade module
- Create our first Jade file
- Create a Node.js program that uses the Jade file
- Compare
jade.compile
,jade.render
, andjade.renderFile
To add a jade
dependency to your project, or if you’re starting from scratch from an empty project folder, do the following:
- Create an empty
node_modules
folder with$ mkdir node_modules
- Install and add
jade
topackage.json
with$ npm install jade –save
. See the results in Figure 4–1 .
Let’s say we have some Node.js script that sends e-mail and we need to use a template to generate HTML dynamically for e-mail. This is how it might look (file jade-example.jade
):
.header
h1= title
p
.body
p= body
.footer
div= By
a(href="http://twitter.com/#{author.twitter}")= author.name
ul
each tag, index in tags
li= tag
In this case, our Node.js script needs to hydrate, or populate, this template with the following data:
- title: string
- body: string
- author: string
- tags: array
We can extract these variables from multiple sources (databases, file systems, user input, and so on). For example, in the jade-example.js
file, we use hard-coded values for title
, author
, tags
, but pass through a command-line argument for body
:
var jade = require('jade'),
fs = require('fs');
var data = {
title: "Practical Node.js",
author: {
twitter: "@azat_co",
name: "Azat"
},
tags: ['express', 'node', 'javascript']
}
data.body = process.argv[2];
fs.readFile('jade-example.jade', 'utf-8', function(error, source){
var template = jade.compile(source);
var html = template(data)
console.log(html)
});
In this way, when we run $ node jade-example.js 'email body'
, we get the output shown in Figure 4–2 .
The “prettified” HTML output is as follows:
<div class="header">
<h1>Practical Node.js</h1>
<p></p>
</div>
<div class="body">
<p>email body</p>
</div>
<div class="footer">
<div><a href="http://twitter.com/@azat_co"> Azat</a>
</div>
<ul>
<li>express</li>
<li>node</li>
<li>javascript</li>
</ul>
</div>
In addition to jade.compile()
, the Jade API has the functions jade.render()
and jade.renderFile()
. For example, the previous file can be rewritten with jade.render()
:
var jade = require('jade'),
fs = require('fs');
var data = {
title: "Practical Node.js",
author: {
twitter: "@azat_co",
name: "Azat"
},
tags: ['express', 'node', 'javascript']
}
data.body = process.argv[2];
//jade.render
fs.readFile('jade-example.jade', 'utf-8', function(error, source){
var html = jade.render(source, data)
console.log(html)
});
Furthermore, with jade.renderFile
, the jade-example.js file
is even more compact:
var jade = require('jade'),
fs = require('fs');
var data = {
title: "Practical Node.js",
author: {
twitter: "@azat_co",
name: "Azat"
},
tags: ['express', 'node', 'javascript']
}
data.body = process.argv[2];
//jade.renderFile
jade.renderFile('jade-example.jade', data, function(error, html){
console.log(html)
});
■Note Jade can also be used as a command-line tool after installing it with the -g
or --global
option via NPM. For more information, run jade -h
or see the official documentation (http://jade-lang.com/command-line/).
To use Jade in a browser, you can use browserify (https://github.com/substack/node-browserify) and its jadeify (https://github.com/substack/node-jadeify) middleware.
■Note To use the same Jade templates on front-end (browser) and server sides, I recommend jade-browser
(https://www.npmjs.org/package/jade-browser) by Storify, for which I was the maintainer for a time during my work there. jade-browser
acts as an Express.js middleware and it exposes server-side templates to the browser along with a helpful utility functions. GitHub: ttps://github.com/storify/jade-browser.
This ends this quick guide on Jade. In the next posts, I’ll cover related topics:
- Handlebars syntax
- Handlebars standalone usage
- Jade and Handlebars usage in Express.js 4
- Project: adding Jade templates to Blog
The link under http://naltatis.github.io/jade-syntax-docs/ leads to http://jade-lang.com/. Maybe you can correct it.
Nice and helpful article… thanks
This is a phenomenal breakdown, thanks. I’ve kicked around Jade plenty of times but kept giving up on certain portions / features of it as I didn’t have time to dig into the nuance. Much appreciated.
Really helpful.
This goes into my bookmarks for sure.
Thanks a lot.
Really good tut, Azat! I’d say is even better than the official Jade tutorial.
I’m adding this to my bookmark. Keep the great work going :)
Awesome tutorial Azad. U have made things very simple
Awsome Tutorial Azat. U have things very simple
there is a code error in the Attributes section
you have this line
button(type=”submit, value=”save”)
I believe you meant
button(type=”submit”, value=”save”)
How do you make Jade render a
or tag?
the closest I've come to make it work is doing something like this:
code = var a = 'foo';
and I have to do that for each newline ofcode. Does anyone know of a better way to do this in Jade?
@Porsha,
you are very, very, very, very welcome!
Thank you. Very nice explanations. I agree, when you don’t like something, it could mean that you just don’t understand it enough. Your tutorial is awesome. Short and thorough. I’m not a fan of Jade, but at least now I understand it a little more. This site will be bookmarked!
100% with you @johnaess
My pet hate is Ruby developers turning perfectly sound HTML into Jade, just because it “takes up less space” or “doesn’t have closing tags”.
The time you (don’t even) save on not having closing tags is offset by the *ridiculous* attribute markup and hoops you have to jump through to put text on multiple lines. Quite apart from the fact it’s really hard to read!
Jade blows. If you want a decent templating system, look no further than Nunjucks. Best of HTML and with brilliant template features.
Thank you! Jade makes much more sense now.
Awesome, when I first heard Jade and saw the syntax, I was like how can this be useful…
And then after this I see that it can do pretty much anything.
Thank you for posting this, very helpful for a newB like me :)
Thank you.
I’m SO with you @johnaess. Doing a udemy course right now on server-side JS with Node, Jade, Backbone, etc., and I am somehow stuck on the ONLY part of this class so far that seemed like we were back in my wheelhouse and I should blow through. Saving keystrokes is great, but then why not use things like SublimeText 3, with tons of packages, drop in snippets, cli, and autocompletions. Seems much more logical than needing to learn a new way to write everything. I am disappointed, too, because some of the dynamic features, etc. sounded like they were going to be huge time savers, etc. :( Their documentation is really poor, too. Seems like they barely skim the surface when explaining things — Thank goodness for tutorials from guys like webapplog.com, huh? :)
Grammer: “TRIAL AND ERROR” and not TRAIL AND ERROR
I was feeling much the same about Jade but after reading this, I’ve changed my mind :)
I dont think Jade is a viable option, most of projects design team are isolated from developeres, those guys dont want or dont need to understand another template language beyond old good HTML! if we try to force it we are just reinventing the wheel using a worst one.
I prefer to stick pure CSS; HTML always as posible, it works and makes me productive. I can get all of those HTML generated by design team and easily put behaviors without any need of REWRITING like such crap jade templates.
Sorry I dont buy it.
I think you have a typo. On this code example:
yes / no
the data-active attribute should have the value of “true” based on the locals passed.
That is the awesomest guide on Jade!
Thanks.
Nice …..
Collective unconscious (Carl Jung)? I’m glad it was useful.
Thanks for this Azat, I was actually just thinking about my dislike for Jade last night. Nice extended cheatsheet :)