Pug (Jade)
Language Name:pugFile Extensions:
.pug, .jadeDescription: Clean, whitespace-sensitive HTML template engine
//- Variables
- const title = "Welcome to Pug"
- const items = ["Apple", "Banana", "Cherry"]
- const user = { name: "Alice", age: 25 }
//- Doctype
doctype html
html(lang="en")
head
meta(charset="UTF-8")
meta(name="viewport" content="width=device-width, initial-scale=1.0")
title= title
body
//- Simple tags
h1.title.primary Welcome
p.description This is a paragraph
//- ID and classes
div#container.wrapper.main-content
h2 Content Section
//- Attributes
a(href="https://example.com" target="_blank") Click here
img(src="image.jpg" alt="Description")
//- Interpolation
p Hello, #{user.name}!
p You are #{user.age} years old
//- Conditionals
if user.age >= 18
p You are an adult
else
p You are a minor
//- Loops
ul.item-list
each item in items
li.item= item
//- Case statement
- const day = "Monday"
case day
when "Monday"
p Start of the week
when "Friday"
p Almost weekend!
default
p Regular day
//- Mixins
mixin button(text, type = "primary")
button(class=`btn btn-${type}`)
= text
+button("Submit")
+button("Cancel", "secondary")
//- Includes (for partials)
//- include header.pug
//- include footer.pug
Haml
Language Name:hamlFile Extensions:
.hamlDescription: HTML abstraction markup language
-# Variables
- title = "Welcome to Haml"
- items = ["Apple", "Banana", "Cherry"]
- user = {name: "Alice", age: 25}
!!! 5
%html{lang: "en"}
%head
%meta{charset: "UTF-8"}/
%meta{name: "viewport", content: "width=device-width, initial-scale=1.0"}/
%title= title
%body
-# Tags with classes and IDs
%h1.title.primary Welcome to Haml
%p.description This is a paragraph
%div#container.wrapper
%h2 Content Section
-# Attributes
%a{href: "https://example.com", target: "_blank"} Click here
%img{src: "image.jpg", alt: "Description"}/
-# Interpolation
%p Hello, #{user[:name]}!
%p You are #{user[:age]} years old
-# Conditionals
- if user[:age] >= 18
%p You are an adult
- else
%p You are a minor
-# Loops
%ul.item-list
- items.each do |item|
%li.item= item
-# Inline content
%p
This is a
%strong bold word
in a sentence.
Handlebars
Language Name:handlebarsFile Extensions:
.hbs, .handlebarsDescription: Semantic templating with mustache syntax
{{!-- Variables --}}
<div class="container">
<h1>{{title}}</h1>
<p>Welcome, {{user.name}}!</p>
{{!-- Conditionals --}}
{{#if user.isAdmin}}
<p>Admin access granted</p>
{{else}}
<p>Regular user</p>
{{/if}}
{{!-- Unless --}}
{{#unless user.isLoggedIn}}
<a href="/login">Please log in</a>
{{/unless}}
{{!-- Loops --}}
<ul class="user-list">
{{#each users}}
<li>
<strong>{{this.name}}</strong> ({{this.age}})
{{#if this.email}}
- {{this.email}}
{{/if}}
</li>
{{else}}
<li>No users found</li>
{{/each}}
</ul>
{{!-- With context --}}
{{#with user}}
<div class="user-profile">
<h2>{{name}}</h2>
<p>Age: {{age}}</p>
<p>Email: {{email}}</p>
</div>
{{/with}}
{{!-- Partials --}}
{{> header}}
{{> navigation}}
{{!-- Helpers --}}
<p>{{toUpperCase user.name}}</p>
<p>{{formatDate createdAt}}</p>
</div>
<script>
// Define data
const data = {
title: 'User Dashboard',
user: {
name: 'Alice',
age: 25,
email: '[email protected]',
isAdmin: true,
isLoggedIn: true
},
users: [
{ name: 'Bob', age: 30, email: '[email protected]' },
{ name: 'Charlie', age: 35 }
]
};
// Custom helpers
Handlebars.registerHelper('toUpperCase', function(str) {
return str.toUpperCase();
});
Handlebars.registerHelper('formatDate', function(date) {
return new Date(date).toLocaleDateString();
});
// Compile and render
const template = Handlebars.compile(document.querySelector('template').innerHTML);
const html = template(data);
document.body.innerHTML = html;
</script>
EJS (Embedded JavaScript)
Language Name:ejsFile Extensions:
.ejsDescription: JavaScript embedded in HTML
<%
// Variables and logic
const title = "Welcome to EJS";
const items = ["Apple", "Banana", "Cherry"];
const user = { name: "Alice", age: 25, isAdmin: true };
%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><%= title %></title>
</head>
<body>
<!-- Output (escaped) -->
<h1><%= title %></h1>
<p>Hello, <%= user.name %>!</p>
<!-- Output (unescaped) -->
<%- "<strong>Bold text</strong>" %>
<!-- Conditionals -->
<% if (user.age >= 18) { %>
<p>You are an adult</p>
<% } else { %>
<p>You are a minor</p>
<% } %>
<!-- Ternary -->
<p>Status: <%= user.isAdmin ? 'Admin' : 'User' %></p>
<!-- Loops -->
<ul>
<% items.forEach(function(item, index) { %>
<li><%= index + 1 %>. <%= item %></li>
<% }); %>
</ul>
<!-- For loop -->
<% for (let i = 0; i < 5; i++) { %>
<div>Item <%= i %></div>
<% } %>
<!-- Functions -->
<%
function formatName(name) {
return name.toUpperCase();
}
%>
<p><%= formatName(user.name) %></p>
<!-- Include partials -->
<%- include('header') %>
<%- include('footer') %>
</body>
</html>
Nunjucks
Language Name:nunjucksFile Extensions:
.njk, .nunjucksDescription: Jinja2-inspired templating
{# Variables #}
{% set title = "Welcome to Nunjucks" %}
{% set user = { name: "Alice", age: 25 } %}
{% set items = ["Apple", "Banana", "Cherry"] %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
{# Output #}
<h1>{{ title }}</h1>
<p>Hello, {{ user.name }}!</p>
{# Filters #}
<p>{{ user.name | upper }}</p>
<p>{{ user.name | lower }}</p>
<p>{{ user.name | capitalize }}</p>
{# Conditionals #}
{% if user.age >= 18 %}
<p>Adult</p>
{% elif user.age >= 13 %}
<p>Teenager</p>
{% else %}
<p>Child</p>
{% endif %}
{# Loops #}
<ul>
{% for item in items %}
<li>{{ loop.index }}. {{ item }}</li>
{% else %}
<li>No items</li>
{% endfor %}
</ul>
{# Macros #}
{% macro button(text, type="primary") %}
<button class="btn btn-{{ type }}">{{ text }}</button>
{% endmacro %}
{{ button("Submit") }}
{{ button("Cancel", "secondary") }}
{# Extends and blocks #}
{% block content %}
Default content
{% endblock %}
{# Include #}
{% include "header.html" %}
</body>
</html>
Liquid
Language Name:liquidFile Extensions:
.liquidDescription: Safe, customer-facing template language
<!-- Variables -->
{% assign title = "Welcome to Liquid" %}
{% assign user = "Alice" %}
{% assign age = 25 %}
{% assign items = "Apple,Banana,Cherry" | split: "," %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<!-- Output -->
<h1>{{ title }}</h1>
<p>Hello, {{ user }}!</p>
<!-- Filters -->
<p>{{ user | upcase }}</p>
<p>{{ user | downcase }}</p>
<p>{{ user | capitalize }}</p>
<!-- Conditionals -->
{% if age >= 18 %}
<p>Adult</p>
{% elsif age >= 13 %}
<p>Teenager</p>
{% else %}
<p>Child</p>
{% endif %}
<!-- Unless -->
{% unless user == "Guest" %}
<p>Welcome back, {{ user }}!</p>
{% endunless %}
<!-- Loops -->
<ul>
{% for item in items %}
<li>{{ forloop.index }}. {{ item }}</li>
{% else %}
<li>No items</li>
{% endfor %}
</ul>
<!-- Case/When -->
{% case age %}
{% when 18 %}
<p>Just became an adult!</p>
{% when 21 %}
<p>Legal drinking age</p>
{% else %}
<p>Age: {{ age }}</p>
{% endcase %}
<!-- Include -->
{% include 'header' %}
</body>
</html>
Other Template Engines
Mustache
Minimal logic-less templates:<div>
<h1>{{title}}</h1>
<p>Hello, {{user.name}}!</p>
{{#items}}
<li>{{.}}</li>
{{/items}}
{{^items}}
<p>No items</p>
{{/items}}
</div>
Eta
Lightweight, fast alternative to EJS:<% /* Variables */ %>
<% const title = "Eta Template" %>
<div>
<h1><%= title %></h1>
<% if (user.isAdmin) { %>
<p>Admin Panel</p>
<% } %>
<% items.forEach(item => { %>
<li><%= item %></li>
<% }) %>
</div>
Twig
PHP-inspired templating:{# Variables #}
{% set title = "Twig Template" %}
{% set user = { name: "Alice", age: 25 } %}
<div>
<h1>{{ title }}</h1>
<p>{{ user.name | upper }}</p>
{% if user.age > 18 %}
<p>Adult</p>
{% endif %}
{% for i in 1..5 %}
<li>Item {{ i }}</li>
{% endfor %}
</div>
Comparison
- Pug
- Handlebars
- EJS
- Nunjucks
Pros:
- Very clean, minimal syntax
- No closing tags
- Powerful mixins
- Whitespace-sensitive
- Learning curve
Pros:
- Logic-less philosophy
- Safe for user content
- Great for client-side rendering
Pros:
- Familiar JavaScript syntax
- Easy to learn
- Full JS power
Pros:
- Powerful features
- Template inheritance
- Rich filter system
Best Practices
Keep Logic Minimal
Keep Logic Minimal
Templates should focus on presentation:
<!-- Good -->
<% if (isLoggedIn) { %>
<p>Welcome back!</p>
<% } %>
<!-- Avoid complex logic -->
<% if (user && user.profile && user.profile.settings) { %>
<!-- Too complex -->
<% } %>
Use Partials
Use Partials
Break templates into reusable parts:
//- header.pug
header
nav
a(href="/") Home
//- main.pug
include header
main
block content
Escape User Content
Escape User Content
Always escape user-provided content:
<!-- Escaped (safe) -->
<%= userContent %>
<!-- Unescaped (unsafe) -->
<%- userContent %>
Related
JavaScript
Use templates with JavaScript
HTML
Standard HTML support
Markdown
Content-focused markup
React
JSX templating