Adding Handlebars to a Rails App With Webpacker

John Hager
3 min readApr 6, 2017

--

I have been trying out the new webpack integration in the Rails 5.1 release candidate 1, and have been quite happy with how it works. Convention over configuration is a big win here, and the default setup will get you up and running in no time.

One thing that you might want to do in your “app-like” or “pack-ed” javascript, that lives in app/javascript and app/javascript/packs is to use some templating engine for building html to use in your JavaScript application.

I’m more or less familiar with handlebars from work that I have done recently with Ember, so I decided to see how easy it is to get webpack to compile handlebars for me. Turns out it was pretty easy, but I want to share with you a short tutorial on how to get it done and how to use it in your JavaScript, since I did struggle with a few things until I understood better what was going on.

So without further preamble, here is how you add handlebars to your Rails app.

Before you start

Check that you have webpacker installed. If you used the --webpack flag when creating your application this should be the case. (Just FYI, I did use this flag, but I still needed to install webpacker manually. Not sure if it was a bug or what.) If you don’t see config/webpack or bin/webpack, bin/webpack-dev-server, and bin/webpack-watcher, then you should run the install script.

# in your rails app directory (Rails 5.1.0.rc1)
$> bin/rails webpacker:install

Setup

First off you need to add the dependencies you will require.

# In your rails app directory (Rails 5.1.0.rc1)
$> yarn add handlebars
$> yarn add handlebars-loader

Now you will be able to require handlebars in your JavaScript. The handlebars-loader package is a webpacker loader that will take care of compiling your handlebars template files into JavaScript.

In order for webpacker to pickup handlebar files and pass them first to the handlebars-loader, we will need to configure a loader in the webpack config. Rails’ default webpack config is set up to add webpack rules from the exports of each file in the config/webpack/loaders directory. (Just FYI, something I noticed is that if you are using vim, webpacker’s webpack-dev-server picks up .swp files, so you need to be careful when restarting the dev-server).

We can add a handlebars loader by creating this file:

// in config/webpack/loaders/handlebars.jsmodule.exports = {
test: /\.hbs$/,
loader: 'handlebars-loader'
}

The test property is a regex that matches the filename of files watched by webpack. I chose to use .hbs but you could change this to use .handlebars or whatever.

The last bit of setup that we need to do is to include our JavaScript in our layout. This is done with the new javascript_pack_tag.

<!-- in the head of app/views/layouts/applicaiton.html.erb -->
<!-- ... -->
<head>
<!-- ... -->
<%= javascript_pack_tag 'application' %>
</head>
<!-- ... -->

Use

Now that we have it all set up, we can start writing handlebars templates.

Here is a quick example:

<!-- in app/javascript/quote.hbs -->
<blockquote>
{{quoteText}}
<small>{{quoteAuthor}}</small>
</blockquote>

We can access this template by requiring it in our JavaScript. The handlebars-loader compiles the template so that it exports a handlebars template function. Here is how you call that function.

// in app/javascript/packs/application.js/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb
const template = require('../quote.hbs');if (document.readyState === 'ready' || document.readyState !== 'loading') {
addQuote();
} else {
document.addEventListener('DOMContentLoaded', addQuote);
}
function addQuote() {
var quote = {
quoteText: "If you want to push, then first pull. If you want to go up, first go down",
quoteAuthor: "Unknown"
};

document.body.innerHTML = template(quote);
}

So you can see that we get the template function through webpacker’s require mechanism, and we call it with the quote object as the first argument. In the {{quoteText}} notation in the handlebars template gets the quoteText property from the quote object.

Now when we hit a route that includes our application pack, the html body tag’s contents will be replaced with the content from our template (oops ;p). Sure this is a contrived example, but I hope that it will be useful for people who are also trying out webpack(er) for the first time like I am.

Thanks for reading!

Resources

--

--