🗿 Jade Guide
Jade is HTML templating with JavaScript, usually used with Node.js. Related to HTML guide.
TODO: Add Jade syntax highlight.
You use tag name and jQuery style selector to specify HTML elements.
div
// <div></div>
div#container
// <div id="container"></div>
div.user-details
// <div class="user-details"></div>
div#foo.bar.baz
// <div id="foo" class="bar baz"></div>
#foo
.bar
// <div id="foo"></div><div class="bar"></div>
// Default tag is <div> but it is good to always include it for consistency.
Nesting is done by indention. Use 2 space indention like in JavaScript.
!!!5
html(lang="en")
head
body
div
// <!DOCTYPE html><html lang="en"><head></head><body><div></div></body></html>
You can add content to elements by one line of text, by dotting or by piping.
// One liner
p wahoo!
// <p>wahoo!</p>
// Dotting
p.
foo asdf
asdf
asdfasdfaf
asdf
asd
// <p>foo asdf
// asdf
// asdfasdfaf
// asdf
// asd
// </p>
// Piping
p
| foo bar baz
| rawr raw rawr
| super cool
| go jade go
// <p>
// foo bar baz rawr raw rawr super cool go jade go
// </p>
// Piping is not required on elements that only accepts text e.g. script.
script
if (foo) {
bar();
}
else {
baz();
}
Unescaped text is prefixed by !
while unbuffered JavaScript is prefixed by -
.
- var html = "<script></script>"
!{html}
// <script></script>
Interpolation is done with #{}
.
- var teejii = { name: 'tj', email: 'tj@vision-media.ca' };
div#user #{teejii.name} <#{teejii.email}>
// <div id="user">
// tj <tj@vision-media.ca>
// </div>
Escaping is done with \
.
p \#{escaped_string}
Element attributes are specified in parenthesis.
a(href='/login', title='View login page') Login
// Boolean attributes
input(type="checkbox", checked)
// Outputs only if someVariable is true.
input(type="checkbox", checked=someVariable)
input(
type='checkbox'
name='agreement'
checked
)
- var user = { id: 12, name: 'tobi' }
a(href='/user/' + user.id)= user.name
a(href='/user/#{user.id}')= user.name
- var bodyClasses = ['user', 'authenticated']
body(class=bodyClasses)
Nested tags
// Following two generate same HTML.
label Username:
input(name='user[name]')
label
| Username:
input(name='user[name]')
ul
li.first
a(href='#') foo
li
a(href='#') bar
li.last
a(href='#') baz
// <ul>
// <li class="first">
// <a href="#">foo</a>
// </li>
// <li>
// <a href="#">bar</a>
// </li>
// <li class="last">
// <a href="#">baz</a>
// </li>
// </ul>
// Block expansion with same output
ul
li.first: a(href='#') foo
li: a(href='#') bar
li.last: a(href='#') baz
Commenting
Normal HTML comments are //
. Silent comments are //-
They work to comment out whole nested branch.
body
//
div#content
h1 Example
// <body>
// <!--
// <div id="content">
// <h1>Example</h1>
// </div>
// -->
// </body>
Conditional comments are supported
head
//if lt IE 8
script(src='/ie-sucks.js')
// <head>
// <!--[if lt IE 8]>
// <script src="/ie-sucks.js"></script>
// <![endif]-->
// </head>
There are case statements.
html
body
friends = 10
case friends
when 0
p you have no friends
when 1
p you have a friend
default
p you have #{friends} friends
// Or with block expansion
friends = 5
html
body
case friends
when 0: p you have no friends
when 1: p you have a friend
default: p you have #{friends} friends
You can use filters to generate content from other precompilers.
// :stylus, must have stylus installed
// :less, must have less.js installed
// :coffeescript, must have coffee-script installed
// :markdown, must have markdown-js, node-discount, or marked installed
body
:markdown
Woah! jade _and_ markdown, very __cool__.
we can even link to [stuff](http://google.com)
Code
Unbuffered JavaScript row is prefixed by -
.
- var foo = 'bar';
- var smilies = {lol: ':D', sad: ':('}
- for (var name in smilies)
p= smilies[name]
- var foo = false;
- if (foo)
ul
li yay
li foo
li worked
- else
p oh no! didnt work
- var items = [1,2,3,4];
- if (items.length)
ul
- items.forEach( function(item) {
li= item
- })
Buffered and escaped code is prefixed by =
.
- var foo = 'bar'
= foo
h1= foo
// bar<h1>bar</h1>
Buffered and unescaped code is prefixed by !=
.
- var aVariableContainingMoreHTML = '<h1>:DSL</h1>';
p!= aVariableContainingMoreHTML
// Following are equal, designer-friendly variants.
- var foo = 'foo ' + 'bar'
foo = 'foo ' + 'bar'
// Thus, previous could be written like.
aVariableContainingMoreHTML = '<h1>:DSL</h1>';
p!= aVariableContainingMoreHTML
Jade has also first-class if, else if, else, until, while, unless
etc.
- var foo = 'bar';
if foo === 'bar'
ul
li yay
li foo
li worked
else
p oh no! didnt work
Iteration
Basic each.
- var items = ['one', 'two', 'three']
ul
each item in items
li= item
// <ul>
// <li>one</li>
// <li>two</li>
// <li>three</li>
// </ul>
Iterating with index.
items = ["one", "two", "three"]
each item, i in items
li #{item}: #{i}
// <li>one: 0</li>
// <li>two: 1</li>
// <li>three: 2</li>
Iterating objects.
obj = { foo: 'bar', bear: 'four'}
each val, key in obj
li #{key}: #{val}
// <li>foo: bar</li>
// <li>bear: four</li>
Nested iteration.
donald = {name: 'Donald', roles:['driver', 'duck']}
mickey = {name: 'Mickey', roles:['detective', 'mouse']}
users = [donald, mickey];
each user in users
each role in user.roles
li= role
donald = {name: 'Donald', roles:['driver', 'duck']}
mickey = {name: 'Mickey', roles:['detective', 'mouse']}
users = [donald, mickey];
for user in users
for role in user.roles
li= role
Conditionals
donald = {name: 'Donald', role:'admin'}
mickey = {name: 'Mickey', role:'detective'}
users = [donald, mickey];
for user in users
if user.role == 'admin'
p #{user.name} is an admin
else
p= user.name
// IS EQUIVALENT TO
donald = {name: 'Donald', role:'admin'}
mickey = {name: 'Mickey', role:'detective'}
users = [donald, mickey];
for user in users
- if (user.role == 'admin')
p #{user.name} is an admin
- else
p= user.name
// Unless
donald = {name: 'Donald', role:'admin'}
mickey = {name: 'Mickey', role:'detective'}
users = [donald, mickey];
for user in users
unless user.isAnonymous
p
| Click to view
a(href='/users/' + user.id)= user.name
Template inheritance
Done with block
and extends
.
// Block is a request for child template.
// Here 2 of 3 blocks provide default content.
// layout.jade
html
head
h1 My Site - #{title}
block scripts
script(src='/jquery.js')
body
block content
block footer
#footer
p some footer content
// my_page.jade
extends layout
block scripts
script(src='/jquery.js')
script(src='/pets.js')
block content
h1= title
each pet in pets
include pet
Blocks can request more blocks.
// extended_layout.jade
extends layout
block content
.sidebar
block sidebar
p nothing
.primary
block primary
p nothing
Append and prepend.
// layout.jade
// Contains JS that is loaded on every page.
html
head
block head
script(src='/vendor/jquery.js')
script(src='/vendor/caustic.js')
body
block content
// flipflop.jade
extends layout
block append head
script(src='/vendor/three.js')
script(src='/game.js')
// flopflip.jade
extends layout
block prepend head
script(src='/vendor/three.js')
script(src='/game.js')
Includes
// Directory structure:
// ./layout.jade
// ./includes/head.jade
// ./includes/foot.jade
// layout.jade
html
include includes/head
body
h1 My Site
p Welcome to my super amazing site.
include includes/foot
// Path is relative to filename option passed to Jade.
// Automatic in Express.
// If you give include an extension like .html, it won't parse it as Jade.
html
body
include content.html
You can use yield
to mark where block given to include is placed.
// layout.jade
html
include head
script(src='/foo.js')
script(src='/bar.js')
body
h1 test
// head.jade
head
yield
script(src='/jquery.js')
script(src='/jquery.ui.js')
Advanced yielding.
// people.jade
users = [{ name: 'Tobi', occupation: 'Ferret' }]
each user in users
.user
include user
// user.jade
h1= user.name
p= user.occupation
// <div class="user">
// <h1>Tobi</h1>
// <p>Ferret</p>
// </div>
Mixins
Mixins are like mini templates.
mixin list
ul
li foo
li bar
li baz
h2 Groceries
mixin list
// <h2>Groceries</h2>
// <ul>
// <li>foo</li>
// <li>bar</li>
// <li>baz</li>
// </ul>
Mixins can have parameters.
mixin pets(pets)
ul.pets
- each pet in pets
li= pet
mixin profile(user)
.user
h2= user.name
mixin pets(user.pets)
jack = {name: 'Jack', pets: ['Don', 'Foo']}
mixin profile(jack)
// <div class="user">
// <h2>Jack</h2>
// <ul class="pets">
// <li>Don</li>
// <li>Foo</li>
// </ul>
// </div>
Jade with make
Use to compile pages/*.jade into pages/*.html files by simply make
.
JADE = $(shell find pages/*.jade)
HTML = $(JADE:.jade=.html)
all: $(HTML)
%.html: %.jade
jade < $< --path $< > $@
clean:
rm -f $(HTML)
.PHONY: clean
Optionally combine with file watchers.
watch make