Sling Academy
Home/Node.js/Express + Handlebars: How to Include Subviews

Express + Handlebars: How to Include Subviews

Last updated: January 18, 2024

Introduction

When building web applications with Express.js and Handlebars, managing templates effectively is crucial to ensure maintainability and scalability. Including subviews within your views is an architectural best practice that compartmentalizes the UI, leading to cleaner and more modular code. In this tutorial, we’re going to cover how to leverage subviews in your Express.js applications using the Handlebars templating engine.

Setting Up an Express.js Project with Handlebars

Before diving into subviews, let’s ensure your environment is properly set up. Begin by initializing a new Node.js project and installing Express.js and express-handlebars, which is a Handlebars view engine for Express. Additionally, install nodemon for better development experience:

$ mkdir express-hbs-subviews
$ cd express-hbs-subviews
$ npm init -y
$ npm install express express-handlebars
$ npm install --save-dev nodemon

Edit your package.json to add a start script:

"scripts": {
 "start": "nodemon app.js"
},

Create an ‘app.js’ file and initialize your Express application with Handlebars as its view engine:

const express = require('express');
const { engine } = require('express-handlebars');

const app = express();

app.engine('handlebars', engine());
app.set('view engine', 'handlebars');
app.set('views', './views');

// ... additional Express middlewares and routes

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
 console.log(`Server is running on port ${PORT}`);
});

Understanding Partials in Handlebars

Before including subviews, it’s important to comprehend the concept of partials in Handlebars. Partials are smaller chunks of markup that can be reused across multiple templates. Create a directory named ‘partials’ inside your ‘views’ directory. For example, if you have a navigation bar common to all pages, make a file named ‘nav.handlebars’.

<nav>
 <!-- Navigation bar HTML -->
</nav>

To use this partial in your templates, firstly register it in your application:

const hbs = engine();
app.engine('handlebars', hbs);
app.set('view engine', 'handlebars');

hbs.handlebars.registerPartial('nav', fs.readFileSync('./views/partials/nav.handlebars', 'utf8'));

Now in any of your main Handlebars files, include the partial like so:

{{> nav}}

Including Subviews

Subviews are quite similar to partials but often pertain to larger sections of content that may even encapsulate partials themselves. For instance, consider a blog site that includes the same sidebar on multiple pages. You could create a ‘sidebar.handlebars’ in the ‘partials’ folder:

<aside>
 <!-- Sidebar content and maybe other partials like '{{> searchForm}}' -->
</aside>

Then, in your main views such as ‘home.handlebars’ and ‘about.handlebars’, you can include this sidebar subview:

{{> sidebar}}

This approach allows you to construct your page layouts with modular sections that can be mixed and matched as needed.

Passing Data to Subviews

Often you’ll need to pass context to your subviews or partials. For example, you can pass data from a route handler to a view with something like:

app.get('/', (req, res) => {
 res.render('home', {
   user: req.user,
   message: 'Handlebars is great!'
 });
});

In the ‘home.handlebars’ template, you can refer to these properties:

{{user.firstName}} - {{message}}

To pass this data down to subviews or partials, use the Handlebars built-in ‘with’ helper:

{{#with user}}
 {{> userProfile}}
{{/with}}

Dynamically Including Subviews

There might be scenarios where you want to dynamically determine which subview to include. You can do so by creating a custom helper. First, register a new helper:

hbs.handlebars.registerHelper('dynamicPartial', function (name) {
 return name;
});

Usage in your templates will look like this:

{{> (dynamicPartial subviewName) }}

Here, ‘subviewName’ is a variable representing the name of the partial you wish to include. It could be something passed from your route handler or conditioned within your view.

Conclusion

Including subviews in your Express and Handlebars templates is an excellent way to build maintainable and modular applications. By following the outlined strategies, you ensure that common elements are reused and state is appropriately managed and passed down, leading to a scalable architecture for your web applications. Experiment with these concepts and build robust Express.js applications with ease.

Next Article: Express + Handlebars: Escaping Sensitive HTML Output & JavaScript

Previous Article: Node.js + Express: How to return CSV files

Series: Node.js & Express Tutorials

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates