hapi — How to Create Jade-Like Layout Blocks in Handlebars

Handlebars is a great templating language with partial view support. It’s highly extensible and adjustable with custom helper functions to add individual features if required.

If you’ve ever worked with the Jade templating engine, you probably know and used the block feature which adds a placeholder into your layout which can be replaced with custom content from child views. Handlebars lacks support for this feature, even though it’s a nice and easy way to add stylesheets and scripts within subviews if necessary.

This guide shows you how to add the Jade-like block functionality to handlebars and how to use it within your layouts.

hapi Series Overview

What’s the Problem?

When using templates within handlebars, you can’t define custom content placeholders which can be replaced with content defined within subviews. This functionality makes sense if you want to add individual stylesheets or scripts from within child views. Specifically, we only need the script to load Disqus comments within the post detail view. We don’t need the script to be loaded on every page and only want it to be added to the rendered HTML when necessary (when reading a specific post).

Solution

To be honest, we were looking for a solution for quite some time. Some days ago, when we finally understood the actual problem, we indeed found a solution. We wanted to implement and started a last research if there is something out there to be reused. And finally, we found the functionality already created by Roman Shtylman within his repository: handlebars-extend-block.

What this small script of 22 lines does: it adds two helper functions to handlebars which extend the template language by two functions block and extend. The block function is an array which can be extended by the extend function.

Hapi View Configuration

The hapi framework isn’t required to add the block functionality to handlebars. We use hapi as our framework of choice and that’s why we use the following code snippet to illustrate the addition of the handlebars-extend-block plugin to the default handlebars package.

const Hapi = require('hapi')  
const Handlebars = require('handlebars');  
const ExtendedHbs = require('handlebars-extend-block');

// Create hapi server instance
const server = new Hapi.Server({  
    host: 'localhost',
    port: 3000
})

async function liftOff() {  
  await server.register({
    plugin: require('vision')  // add template rendering support in hapi
  })

  server.views({
    engines: {
      html: ExtendedHbs(Handlebars)
    },
    path: 'views',
    layoutPath: 'views/layout',
    layout: 'default',
    partialsPath: 'views/partials'
  })
}

liftOff()  

As you can see in the snippet above, we pass the handlebars object into the extend() function. The extend() function returns the decorated handlebars object which can be passed into hapi as the view rendering language.

Layout Templates

Let’s look at some precise template layouts and illustrate the new functionality with the help of an example. We’re using a default layout with two defined blocks: css and js. The {{{content}}} placeholder is used within the default layout to put the rendered HTML at its place.

The following snippet shows the default layout:

Default Layout

<!DOCTYPE html>  
<html>  
<head>  
    {{! Document Settings }}
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Extend Handlebars Layouts</title>

    <link rel="stylesheet" href="/assets/css/screen.css" />
    {{{block "css"}}}
</head>  
<body>

    {{! Everything else gets inserted here }}
    {{{content}}}

    <script src="/assets/js/jquery.min.js"></script>
    {{{block "js"}}}

</body>  
</html>  

The css and js blocks will be automatically replaced with the defined content from within subviews. Handlebars will render the block contents if you specified one.

Partial Layout The partial layout below extends the css as well as the js block. We’re adding our custom CSS stylesheet and an additionally javascript file. You already guessed it, this partial view is just used for illustration purposes …

{{!< layout/default}}
{{! The tag above means - insert everything within this file into the {{{content}}} of the default.html template }}

{{#extend "css"}}
<link rel="stylesheet" href="/assets/css/my-additional.css" />  
{{/extend}}

<h1>This is the title</h1>  
<p>Hello from partial view</p>

{{#extend "js"}}
<script src="/assets/js/my-personal-javascript.js"></script>  
{{/extend}}

Result

The rendered HTML will look like this. The defined blocks within the default layout will be replaced with the content from child views. If you don’t extend the blocks within the child views, handlebars will omit the specific block and leave it empty.

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Extend Handlebars Layouts</title>

    <link rel="stylesheet" href="/assets/css/screen.css" />
    <link rel="stylesheet" href="/assets/css/my-additional.css" />
</head>  
<body>

    <h1>This is the title</h1>
    <p>Hello from partial view</p>

    <script src="/assets/js/jquery.min.js"></script>
    <script src="/assets/js/my-personal-javascript.js"></script>

</body>  
</html>  

Conclusion

As you saw within the examples above, the block helpers to manipulate the default layout from within subviews benefits when used correctly. You should add stylesheets and javascript files which is required on every page within the default layout. Extending specific views with additional functionality like comments is a great use case to use layout blocks and add required scripts or styling.

We hope this helps and if you run into issues when applying this guide to your project, please don’t hesitate to tweet us @futurestud_io or leave a comment.


Additional Resources

Explore the Library

Find interesting tutorials and solutions for your problems.