Webpack with Rails, Part 1:
Using an External Tool with Sprockets

Warning: This post is very old. It may be out of date, and links in it may not work. Continue at your own risk.

I don’t like Sprockets, but it’s the easiest and best choice for lots of Ruby on Rails projects. When looking for a better way to manage my JavaScript assets and an improved developer story, my current tool of choice is webpack. While I wish Rails supported drop-in asset pipelines the way that Phoenix does, it’s not hard to use webpack with Rails.

Disclaimer: There are lots of other guides for getting webpack working with Rails. Those guides I’ve read missed details I felt were important, or configured webpack in ways that I was not happy with. I wrote this as a guide to provide the reader with a solid starting point that wasn’t too opinionated.

I had the following requirements:

This article was written using Rails 4.2.4 and should work for any version of Rails 4.2, and should need little if any modification for working against other 4.x versions. I used webpack 1.12.2, and babel-loader 5.3.2.

Install webpack

Run npm init and follow the instructions. Next, install webpack and babel-loader by running npm install --save babel-loader webpack.

Configure webpack

Here’s a minimal webpack.config.js. Drop it into the root of your project.

// webpack.config.js

"use strict";

var path = require('path');
var config = module.exports = {};

config.context = __dirname;

// Specify your entries, I store all my webpack managed JavaScript in
// app/webpack, as per my earlier requirements.
config.entry = {
  cart: './app/webpack/cart.js'
};

// This outputs an entry named 'foobar' into
// app/assets/javascripts/entries/foobar.js.
config.output = {
  path: path.join(__dirname, "app/assets/javascripts/entries"),
  filename: "[name].js"
}

// Use babel-loader for our *.js files.
config.module = {
  loaders: [
    { test: /\.js$/,
      exclude: /node_modules/,
      loader: "babel-loader" }
  ]
}

Rails time

Now, if you write some ES6 in app/webpack/cart.js you can run node node_modules/.bin/webpack and your entry will be compiled to app/assets/javascripts/entries/cart.js. (You may need to create that folder.)

// app/webpack/cart.js

// Write some ES6!
let foo = "bar";
console.log("bar");

To include this entry in your Rails views/layouts, you’ll need to open up config/initializers/assets.rb and add entries/cart.js to your procompile list, and then restart your rails server.

# config/initializers/assets.rb

Rails.application.config.assets.precompile += %w( entries/cart.js )

You can include the javascript tag as you would any other, <%= javascript_include_tag "entries/cart" %> if you’re using ERB.

Finishing touches

Since you’re far too lazy/efficient run webpack every time you modify your assets, you want to run the watcher. Open up your package.json and add a scripts entry:

// package.json

{
  // ...
  "scripts": {
    "watch": "webpack --watch --colors --progress"
  },
  // ...
}

This allows you to start the webpack watcher by running npm run watch. Make sure to include this in your README so new users on the project know how to get things working.

Deployment

I won’t include any specifics for handling deployment as the details would vary too much between environments. In most configurations you’ll want to either override/modify your assets:precompile rake task to run webpack first, or modify your deploy system to run webpack before assets:precompile is run.

Write ES6, Be Happy

Webpack and Babel provide a powerful system for breaking up your JavaScript into self-contained entry points and isolating individual components, with an excellent developer story.

Webpack allows you to build much more complex pipelines to meet your application’s requirements. For example Webpack’s ecosystem works exceptionally well when building React and Flux/Redux applications, offering tools like React Hot Loader.

I provide technical leadership, risk assessment, project planning, training, and additional development to software teams. If you need help taking a project from idea through execution, get in touch.