Template engines enable you to use static template files in your application. At runtime, the template engine replaces variables in a template file with actual values and transforms the template into an HTML file sent to the client.
Popular Template Engines
Pug Clean, whitespace-sensitive syntax
EJS Embedded JavaScript templates
Handlebars Minimal logic, mustache compatible
Setting Up a Template Engine
Install the template engine
Configure Express
const express = require ( 'express' );
const path = require ( 'path' );
const app = express ();
// Set views directory
app . set ( 'views' , path . join ( __dirname , 'views' ));
// Set view engine
app . set ( 'view engine' , 'ejs' );
Create template files
Create a file at views/index.ejs: <! DOCTYPE html >
< html >
< head >
< title > < %= title %> </ title >
</ head >
< body >
< h1 > < %= header %> </ h1 >
< ul >
< % users.forEach(function(user) { %>
< li > < %= user.name %> - < %= user.email %> </ li >
< % }); %>
</ ul >
</ body >
</ html >
Render the template
app . get ( '/' , function ( req , res ) {
res . render ( 'index' , {
title: 'My App' ,
header: 'Users' ,
users: [
{ name: 'Tobi' , email: '[email protected] ' },
{ name: 'Loki' , email: '[email protected] ' }
]
});
});
EJS Templates
EJS (Embedded JavaScript) uses plain JavaScript in templates.
Basic Syntax
<!-- Output escaped value -->
< %= value %>
<!-- Output unescaped value -->
< %- value %>
<!-- JavaScript code -->
< % if (user) { %>
< h2 > Hello < %= user.name %> </ h2 >
< % } %>
<!-- Loop through array -->
< ul >
< % items.forEach(function(item) { %>
< li > < %= item %> </ li >
< % }); %>
</ ul >
Custom File Extensions
Register EJS with a custom extension:
const ejs = require ( 'ejs' );
// Use .html extension instead of .ejs
app . engine ( 'html' , ejs . __express );
app . set ( 'view engine' , 'html' );
// Now you can use .html files
res . render ( 'users.html' , { users: users });
The .html extension makes templates easier to edit in IDEs with HTML syntax highlighting.
Pug Templates
Pug (formerly Jade) uses indentation-based syntax.
Configure Express
app . set ( 'view engine' , 'pug' );
Create Pug template
Create views/index.pug: doctype html
html
head
title = title
body
h1 = header
ul
each user in users
li = user . name + ' - ' + user . email
Handlebars Templates
Handlebars provides minimal logic templates.
npm install express-handlebars
const exphbs = require ( 'express-handlebars' );
app . engine ( 'handlebars' , exphbs . engine ());
app . set ( 'view engine' , 'handlebars' );
<! DOCTYPE html >
< html >
< head >
< title > {{ title }} </ title >
</ head >
< body >
< h1 > {{ header }} </ h1 >
< ul >
{{ #each users }}
< li > {{ name }} - {{ email }} </ li >
{{ /each }}
</ ul >
</ body >
</ html >
Custom Template Engines
You can register custom template engines:
const fs = require ( 'fs' );
const marked = require ( 'marked' );
const escapeHtml = require ( 'escape-html' );
// Register .md as a template engine
app . engine ( 'md' , function ( path , options , fn ) {
fs . readFile ( path , 'utf8' , function ( err , str ) {
if ( err ) return fn ( err );
// Parse markdown and replace variables
const html = marked . parse ( str ). replace ( / \{ ( [ ^ } ] + ) \} / g , function ( _ , name ) {
return escapeHtml ( options [ name ] || '' );
});
fn ( null , html );
});
});
app . set ( 'view engine' , 'md' );
app . get ( '/' , function ( req , res ) {
res . render ( 'index' , { title: 'Markdown Example' });
});
Layouts and Partials
<!-- views/header.ejs -->
< header >
< h1 > < %= siteName %> </ h1 >
</ header >
<!-- views/index.ejs -->
< %- include('header', { siteName: 'My Site' }) %>
< main >
< p > Content here </ p >
</ main >
< %- include('footer') %>
//- views/layout.pug
doctype html
html
head
title = title
body
block content
//- views/index.pug
extends layout
block content
h1 Welcome
p This is the index page
Passing Data to Templates
// Pass individual values
res . render ( 'index' , {
title: 'My Page' ,
user: req . user
});
// Use res.locals for values available in all templates
app . use (( req , res , next ) => {
res . locals . siteName = 'My Website' ;
res . locals . currentUser = req . user ;
next ();
});
Use res.locals to set variables that should be available in all templates, such as the current user or site configuration.
Caching Templates
Enable view caching in production to improve performance:
if ( app . get ( 'env' ) === 'production' ) {
app . enable ( 'view cache' );
}
Best Practices
Escape user input to prevent XSS attacks
Use partials/includes for reusable components
Enable caching in production
Keep logic minimal in templates
Use layouts to avoid duplicating HTML structure
Set appropriate content type for non-HTML templates