hapi — How to Render and Reply Views

Within the previous tutorial, you’ve seen how to serve static files using hapi. HTML files also apply to the static file concept and you could use hapi to reply requests with rendered HTML content. Nonetheless, if you’re on the search for a more dynamic approach related to views, hapi got your back!

This guide walks you through the setup of hapi to render views on server side and reply them for a given request.

Before diving into the details, have a look at the series outline and find posts that match your interests and needs.

hapi Series Overview

Plugin Required: Vision

In case you followed this series, you already know about hapi’s powerful plugin system and that the framework’s core is sort of stripped down to the minimum required.

To add view support for your hapi server, the vision plugin needs to be registered to your hapi server instance. Vision adds template rendering support for multiple template engines like Handlebars, Pug (previously Jade), EJS, Mustache, and many more. We’re currently not aware of any other plugin that would add template rendering support to hapi. If you know one, please share a linke within the comments :)

Alright, add vision as a dependency to your project:

npm install -S vision  
# -S is equal to --save

As soon as the dependency installation has finished, you can use the package within your application. Prepare your server for view support by registering the vision plugin.

hapi v17

const Hapi = require('hapi')

// create new server instance
const server = new Hapi.Server()

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

liftOff()  

hapi v16

const Hapi = require('hapi')  
const Vision = require('vision')

// create new server instance
const server = new Hapi.Server()

// register vision to your server instance
server.register(Vision, function (err) {  
  if (err) {
    console.log('Cannot register vision')
  }
})

Vision decorates your server, request and reply interfaces with additional methods to handle view engines and ultimately respond a request with a rendered template.

Once the plugin is registered successfully, you’re able to configure the view settings.

Prepare Your Server

Vision decorates your server instance and adds the views() method that allows you to configure template support by defining your desired engine, the path to view files and further options.

Within the following, we’ll use handlebars as the template engine of choice. As already mentioned, you’re free to use your favorite engine and certainly hapi supports it. Using handlebars requires us to add the package as a project dependency: npm install -S handlebars. Further, we’ll use the following structure where view files are located within a views folder besides the server’s file.

my-project-folder  
|- server.js
|- views
  |- layout.html
  |- index.html

The code block above depicts the mentioned folder structure. The server.js file contains all code to spin up your hapi instance. The views folder contains the default layout (default.html) and an additional view (index.html). At this point, the contents of the individual view files don’t matter. Going in detail on the views itself would exceed the scope of this tutorial. There’s another guide waiting for you that is geared towards the views itself and we’ll tell you where to find it later in this section.

The following code snippet illustrates how to configure handlebars as the template rendering engine and also the path to view files including a default layout that will be used for every view.

hapi v17

const Hapi = require('hapi')  
const Handlebars = require('handlebars')

// create new server instance
const server = new Hapi.Server()


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

  // configure template support   
  server.views({
    engines: {
      html: Handlebars
    },
    path: __dirname + '/views',
    layout: 'layout'
  })
}

liftOff()  

hapi v16

const Hapi = require('hapi')  
const Vision = require('vision')  
const Handlebars = require('handlebars')

// create new server instance
const server = new Hapi.Server()

// register vision to your server instance
server.register(Vision, function (err) {  
  if (err) {
    console.log('Cannot register vision')
  }

  // configure template support   
  server.views({
    engines: {
      html: Handlebars
    },
    path: __dirname + '/views',
    layout: 'layout'
  })
})

Successfully registering vision to your server instance allows you to make use of the new server.views() method to configure template rendering support. Please remember the depicted project structure where view files are located in a views folder aside the server.js file.

The path option takes the path to your root folder where any view file is located (as .html files). Also, you’re allowed to define a default layout that contains recurring blocks like header, content wrapper, footer, etc. There’s another tutorial going in detail on how to create dynamic handlebars layouts using hapi that might be interesting for you.

Default Layout Name

The layout option takes either a boolean (true/false) or the name of your default layout as a string parameter. If you’re using the boolean, please name your default layout as default.html. Using the layout’s name, you can just provide the name like this: layout: 'my-default-layout'.

Render and Reply Views

Enough theory and configuration, we know you’re interested in how to reply views. You can either use reply.view() or a view handler for your route configuration. Vision decorated the reply interface and added the view() method and also provides the handler functionality to reply views directly.

Reply Rendered Views Using reply.view()

This section assumes that you’re familiar with routing in hapi. If you want to recap your knowledge, just follow the linked tutorial.

The reply.view() method takes two arguments: first, the view’s file name and second, additional context data. The view name is required so that vision can pick the correct file from your defined view folder path. Context data is beneficial if you want to render placeholders in your views dynamically with user or context-specific data.

hapi v17

server.route({  
  method: 'GET',
  path: '/',
  handler: (request, reply) => {
    var data = { message: 'Hello from Future Studio' }

    return h.view('index', data)
  }
})

hapi v16

server.route({  
  method: 'GET',
  path: '/',
  handler: (request, reply) => {
    var data = { message: 'Hello from Future Studio' }

    reply.view('index', data)
  }
})

The route configuration above will render the index view file and pass an additional data object to the view. Passing data to views is an extensive topic that is tightly coupled with your rendering engine. You’ll find more information on how to replace placeholders with actual data within the documentation of your chosen template engine.

View Handler

If you just want to render a view without passing and replacing dynamic attributes, just go ahead and define your handler as an object and pass the view property with the related view name. Hapi will search for the given view, render the template and reply the HTML content.

server.route({  
  method: 'GET',
  path: '/',
  handler: {
    view: 'index'
  }
})

You can also pass static context data using a view handler. Use the view property and pass an object containing fields for template and context. template holds the view name and context is the data object that can be used within your views to exchange placeholders with your desired data.

server.route({  
  method: 'GET',
  path: '/',
  handler: {
    view: {
      template: 'mytemplate',
      context: {
        title: 'My Template Title',
        message: 'This is a message for my view!'
      }
    }
  }
})

Supported View Engines

Vision ships with support for various template rendering engines. The list below outlines the most common engines that are supported out of the box.

Within Vision’s GitHub repository you’ll find example configuration for all of the listed engines.

Layouts

Throughout this guide, we’ve linked to another tutorial that leads you through the details on how to create a dynamic view layout with header, content and footer support. We’ve further guides that explain how to create and use partial views and helper functions within views that get rendered on server side using hapi.

Outlook

This step-by-step guide walked you through the setup of template rendering support within a hapi server. You’ve learned about the vision plugin that decorates the server, request and reply interfaces and with that adds view support for various template formats. There’s a bunch of supported engines that come at no cost. You’re free to create your own package to bring vision and another template engine together.

In case you want to go in detail on views and create dynamic layouts, follow the linked guides on partial views and helper functions. There’s a lot to discover!

Besides nice looking websites, you want to serve static files like custom CSS and JS, and of course images! Head right to the next tutorial showing you how to serve files in hapi.

We appreciate feedback and love to help you if there’s a question on your mind. Let us know in the comments or on twitter @futurestud_io.

Make it rock & enjoy coding!


Additional Resources

Explore the Library

Find interesting tutorials and solutions for your problems.