Learn how to integrate Handlebars with Express.js to create dynamic, server-side rendered HTML pages. This tutorial guides you through setting up Handlebars, creating templates, and passing data from Express to Handlebars for efficient content rendering.
What is Handlebars?
Handlebars is a templating engine that allows you to build reusable HTML templates. Known for its simplicity, Handlebars uses {{ ... }}
expressions to bind data into templates, making it straightforward to render HTML on the server. When combined with Express.js, Handlebars can dynamically render content based on data passed from the backend, creating a flexible and customizable user experience.
Install Express and Handlebars
To get started, ensure that Node.js is installed. Then, create a new Express project and add the required dependencies:
npm init -y
npm install express express-handlebars
Configure Handlebars as the View Engine in Express
In your main server file (often app.js
or server.js
), configure Express to use Handlebars as its templating engine.
const express = require('express');
const exphbs = require('express-handlebars').create(); // Ensure compatibility with latest versions
const app = express();
// Set up Handlebars as the templating engine
app.engine('handlebars', exphbs.engine);
app.set('view engine', 'handlebars');
// Specify a custom directory for Handlebars views (optional)
app.set('views', __dirname + '/views');
// Serve static files
app.use(express.static('public'));
// Pass global data to all templates
app.use((req, res, next) => {
res.locals.siteName = 'Your Company';
next();
});
Handlebars Project Structure
Here's an example structure for an Express project using Handlebars, with directories for templates, reusable partials, and static assets:
express-handlebars-app/
├── views/
│ ├── partials/ # Reusable templates like headers or footers
│ │ ├── header.handlebars
│ │ └── footer.handlebars
│ └── pages/ # Individual page templates
│ ├── home.handlebars
│ ├── items.handlebars
│ └── products.handlebars
├── public/ # Static files (CSS, JS, images)
│ ├── css/
│ ├── js/
│ └── images/
└── app.js # Main application file
Create Handlebars Templates
Inside the views/pages
directory, create a template file (home.handlebars
):
<!-- views/pages/home.handlebars -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{title}}</title>
<link rel="stylesheet" href="/css/styles.css">
</head>
<body>
{{> header}}
<h1>{{message}}</h1>
<p>This is a dynamic page rendered with Handlebars and Express.js.</p>
{{> footer}}
</body>
</html>
In this template, {{title}}
and {{message}}
are dynamic values passed from Express to Handlebars.
Handlebars Syntax Basics
Handlebars uses simple expressions and blocks to render data and perform basic logic.
- Plain Text: Anything outside
{{ ... }}
is rendered as regular HTML. - Dynamic Variables:
{{variable}}
inserts a dynamic value from Express.<h1>{{title}}</h1>
- Conditionals: Use
{{#if}}
and{{else}}
for conditional rendering.{{#if user}} <p>Welcome, {{user.name}}!</p> {{else}} <p>Please log in.</p> {{/if}}
- Loops: Use
{{#each}}
to loop over arrays and render each item.<ul> {{#each items}} <li>{{this}}</li> {{/each}} </ul>
Passing Data from Express to Handlebars
In Express, use res.render()
to pass data to a Handlebars template. The data is passed as an object, and all key-value pairs in the object become accessible in the template.
app.get('/', (req, res) => {
res.render('pages/home', {
title: 'Welcome',
message: 'Dynamic content with Handlebars and Express!'
});
});
Using JavaScript Logic in Handlebars
Handlebars allows basic logic, such as conditionals and loops, directly in templates.
// Define a route for items
app.get('/items', (req, res) => {
const items = ['Laptop', 'Smartphone', 'Tablet'];
res.render('pages/items', { items });
});
In items.handlebars
:
<!-- views/pages/items.handlebars -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Items List</title>
</head>
<body>
<h1>Items:</h1>
<ul>
{{#each items}}
<li>{{this}}</li>
{{/each}}
</ul>
</body>
</html>
Reusable Partials
Handlebars supports partials, which are reusable components that make it easy to keep code organized. Partials are useful for elements like headers, footers, or navigation bars.
Creating Partials
To create reusable header and footer components, add two .handlebars
files in the partials
directory:
- Header Partial (
header.handlebars
):
<!-- views/partials/header.handlebars --> <header> <nav> <a href="/">Home</a> <a href="/about">About Us</a> </nav> </header>
- Footer Partial (
footer.handlebars
):
<!-- views/partials/footer.handlebars --> <footer> <p>© 2024 Your Company</p> </footer>
Using Partials in Main Templates
To include a partial in a template, use the {{> partialName}}
syntax.
<!-- views/pages/home.handlebars -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{title}}</title>
</head>
<body>
{{> header}}
<h1>Main Content</h1>
<p>Welcome to our dynamic page rendered with Handlebars and Express.js.</p>
{{> footer}}
</body>
</html>
With this setup, the header.handlebars
and footer.handlebars
partials are automatically included in any template that references them, helping to keep your code organized and manageable.
Complete Example
Here's how to combine everything into a simple product catalog:
Setting Up app.js
Create an Express server in app.js
, set up Handlebars as the templating engine, define sample data, and create a route to display a list of products.
const express = require('express');
const exphbs = require('express-handlebars');
const app = express();
// Set up Handlebars as the templating engine
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');
// Route to render the home page
app.get('/', (req, res) => {
res.render('pages/home', { title: 'Welcome', message: 'Dynamic content with Handlebars and Express!' });
});
// Route to render a list of items
app.get('/items', (req, res) => {
const items = ['Laptop', 'Smartphone', 'Tablet'];
res.render('pages/items', { items });
});
// Sample product data for product catalog
const products = [
{ id: 1, name: 'Laptop', price: 75000 },
{ id: 2, name: 'Smartphone', price: 50000 },
{ id: 3, name: 'Tablet', price: 35000 }
];
// Route to render the products page
app.get('/products', (req, res) => {
res.render('pages/products', {
title: 'Product Catalog',
products: products
});
});
// Start the server on port 3000
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});
Creating the products.handlebars
Template
In the views/pages
directory, create a products.handlebars
file to display the product catalog. Use partials for the header and footer to keep the template organized.
<!-- views/pages/products.handlebars -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{title}}</title>
</head>
<body>
{{> header}}
<main>
<h1>{{title}}</h1>
<div class="products">
{{#each products}}
<div class="product">
<h2>{{name}}</h2>
<p>Price: ₹{{price}}</p>
</div>
{{/each}}
</div>
</main>
{{> footer}}
</body>
</html>
Conclusion
In this tutorial, you've learned how to set up and configure Handlebars as a templating engine in Express.js, create and render dynamic HTML pages, pass data from Express to Handlebars, and use basic JavaScript logic in templates with loops, conditionals, and partials. With this knowledge, you can now build data-driven, server-side rendered applications with flexible and reusable content.