Setting Up Webpack
with Rails

Organize your application’s dependencies
and speed up your team.

In Ruby on Rails, convention over configuration is the paradigm. The convention in Rails for managing JavaScript dependencies is metacomments in JavaScript manifest files. The scripts are then loaded by Sprockets.

While this works great for smaller applications, larger applications have more dependencies. When the list of required scripts grows long, figuring out which files depend on each other grows increasingly time consuming. Any reorganization beyond adding files to the list is a pain, because you need to search your entire JavaScript codebase to know where a script is being used.

Modular JavaScript

How do we make it clear which files depend on each other? One way is to divide our JavaScript into modules that can be loaded with locally specified dependencies. Loading dependencies in each file also keeps the global scope clean. There are many tools to facilitate this type of script loading (e.g. Browserify, RequireJS, and webpack) and there are two styles (CommonJS and AMD) for loading modules.

At Brigade we wanted a JavaScript module bundler that would:

  • Integrate simply with Rails
  • Allow some JavaScript to be served via Sprockets
  • Support CommonJS module definitions

We found that webpack offered the features we were looking for.

How Does Webpack Work?

Similar to the way that all scripts are listed in a JavaScript manifest when using Sprockets, all dependencies are listed in an ‘entry point’ script when using webpack. For example, let’s say we want to load an ‘init.js’ script that will render a view on the page. We would require it in the entry point script. Inside the ‘init.js’ file, we require the JavaScript view that will be rendered on the page, as well as a model that will be passed to the view:

Loading files in our entry point for webpack.
In ‘app/assets/javascripts/’

The JavaScript view and model, in turn, list their own dependencies, which in turn may list more dependencies, and so on; it’s turtles all the way down. Webpack recursively finds all the required dependencies and concatenates them into one “bundle”, outputting the code so that it’s all loaded in the correct order.

Whatever the ‘module.exports’ is set to in each of the required files will be wrapped in an anonymous function, and these functions are stored in an array in no particular order. Then, instead of requiring a filename, they are referenced by their index in the array, and the contents are loaded as needed.

Once one of the callbacks is run, it is marked as ‘loaded’ and its ‘exports’ are cached for the next time it is referenced in your code. Here is a simplified example of how the bundled version works:

A simplified and annotated example of the ‘bundle’ file that webpack automatically generates.

Setting Up Webpack

Setting up webpack was simple as running

installing webpack via npm

and writing the ‘webpack.config’ to output javascript in the right place. We’ve embedded an example config file below.

Webpack configuration file with basic settings to work with Ruby on Rails and use the es6-loader.

Integrating Webpack With Rails

Because we use a hybrid system that utilizes both the Sprockets and webpack, our JavaScript is built and served in many steps:

  1. Webpack runs and compiles a bundle based on the scripts required in the entry point script.
  2. The bundle compiled by webpack is required in the JavaScript manifest, along with any scripts that are provided directly by gems.
  3. Rails uses Sprockets to compile and serve scripts listed in the JavaScript manifest.

Developing with Webpack

When setting up a development environment, or making changes to our JavaScript, we need to run webpack in order to generate the bundled version of our scripts.

Fortunately, webpack has a “watch mode” where it will continuously watch your JavaScript files and re-compile the bundle when a change is detected. In order to make it easier to start this process whenever running the Rails server, we use foreman to start these and other relevant processes with one command.

Using Loaders With Webpack

While processing and concatenating your scripts, webpack can also minify or make other types of changes to your code using loaders.

Anyone can write a loader for webpack, because it is modular in nature. There are dozens of webpack loaders that add a range of features, and being able to write your own loaders makes it flexible.

One of our favorite loaders is the es6 loader, which enables us to use all kinds of fun ECMAScript 6th edition (ES6) syntax in our code. ES6 is the latest proposed standard for ECMAScript, which is implemented as JavaScript in most browsers and JScript in Internet Explorer. The features proposed in the ES6 draft are not currently supported by all browsers, but our developers like to play with the latest toys. The “fat arrow function” is one example of a new JavaScript feature we have started using:

An example of how webpack’s es6-loader transpiles our ES6 code.

Because it is automatically bound to the enclosing scope, it helps us avoid errors caused by forgetting to bind callbacks to ‘this’. It’s also a concise shorthand for anonymous functions.

Gotchas

We considered completely replacing Sprockets with a tool like webpack. In our case, there was the added challenge of using some scripts that are provided directly by gems. For example, we use a gem called js-routes that gives our JavaScript access to Rails URL helpers. Adding a metacomment is the way to make the URL helpers available in our JavaScript:

Including the js-routes helper.
In ‘app/assets/javascripts/’

We use several gems that load JavaScript in this way, so we wanted to keep loading some with Sprockets and use a module bundler for the rest.

Choose Wisely

Making technical decisions is all about balancing sets of tradeoffs. It’s worth doing a little research and using the tool best suited to your project’s needs.

We’ve used Sprockets to load JavaScript in the past, and we’ve seen how moving to webpack improved the organization of our application and allows us to work faster. I hope that by sharing our perspective I helped you improve your JavaScript build system too.

Webpack with Rails Demo Repository

(This section added on Thursday, Sept. 4th.)

A reader requested a full example of how to set up Ruby on Rails with webpack, so I have created a sample repository. Hopefully it will help demonstrate one way to set this up!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Flarnie Marchan

Software Engineer at Chegg. Formerly React Core member. Views are my own. Find me at https://github.com/flarnie