Understanding Webpacker in Rails 6

Starting with Rails 6, Webpacker is the default JavaScript compiler. It means that all the JavaScript code will be handled by Webpacker instead of the old assets pipeline aka Sprockets. Webpacker is different from asset pipeline in terms of philosophy as well as implementation. In this blog post, we will learn about how Webpacker goes about handling JavaScript.

What is Webpacker

webpacker is a gem which wraps webpack - the popular JavaScript tool used for managing and bundling JavaScript code - and provides helpers to use the webpack in our Rails applications. In simple words it provides Rails way of using webpack. This is very simple definition for a tool which is very powerful but that is enough for us as of now.

Webpacker wraps webpack in a Ruby gem and provides helpers to use the output from Webpacker in the Rails application.

When you create a new Rails 6 app, you will see following output in the console.

$ rails webpacker:install

RAILS_ENV=development environment is not defined in config/webpacker.yml, falling back to production environment
      create  config/webpacker.yml
Copying webpack core config
      create  config/webpack
      create  config/webpack/development.js
      create  config/webpack/environment.js
      create  config/webpack/production.js
      create  config/webpack/test.js

You will also find webpacker gem in the Gemfile by default. The rails new command will also install tons of npm packages via yarn.

Old applications which are upgraded to Rails 6, do not get webpacker gem installed by default. You need to manually include it in Gemfile and then run rails webpacker:install. We will cover how to use Webpacker in an existing Rails application in a latter blog post.

New destination for the JavaScript code

Before Rails 6, all the JavaScript code is supposed to be in app/assets/javascripts. But in a Rails 6 app, the app/assets/javascripts directory does not even exist. Instead, we have app/javascript directory to host all the JavaScript code. The name is such that it contains the JavaScript part of the application which can be all the code for the frontend part of your application.

Let's go through the contents of this directory in a empty Rails 6 application.

Projects/scratch/better_hn  master ✗ 2.6.3                                                        ◒
▶ tree app/javascript
├── channels
│   ├── consumer.js
│   └── index.js
└── packs
    └── application.js

2 directories, 3 files

It contains two directories, channels and packs. The channels directory is generated by Action Cable component of Rails. We can ignore it safely for now. The packs directory is significant for us so let's see what it contains.

// app/javascript/application.js

What is a pack?

webpack has a concept of entry points which are the files that it looks for first when it starts compiling your JavaScript code. Webpacker gem creates the application pack in the form of this application.js file under app/javascript/packs. If you remember the assets pipeline, this file is equivalent to the app/assets/javascripts/application.js file.

The application pack generated by Rails contains code related to Rails components such as turbolinks, Active Storage and Action Cable.

You will notice that all the Rails frameworks which have JavaScript component such as Rails-UJS, Turbolinks, Active Storage are migrated to support Webpacker. Even the new frameworks introduced in Rails 6 such as Action Text work with Webpacker.

So the application pack is the entry point for all of your JavaScript code. We can create custom packs and place them in the app/javascript/packs directory and Webpacker will find them happily while compiling.

This setting is configured by Webpacker for us in the file config/webpacker.yml

# config/webpacker.yml
5:  source_entry_path: packs

If we want webpack to look for additional directories for the JavaScript code, we can configure the setting resolved_paths in the config/webpacker.yml. The file is pretty self-explanatory in terms of the configuration options.

Compiling the JavaScript code

The next natural step is to look into how to compile the JavaScript code using Webpacker and webpack. In development mode, you don't have to do anything. When you run the rails server, the compilation happens during the request similar to how it used to work with assets pipeline.

Live reloading using webpack-dev-server

Webpacker generates a file bin/webpack-dev-server which can be used for live reloading in the development phase. We have to run the webpack-dev-server separately for this purpose and then we can see live reloading and hot module replacement in action.

Production mode

In production, webpacker appends webpacker:compile task to the assets:precompile task. So if your build pipeline runs assets:precompile task it will take care of compiling files to be compiled by webpack as well. As wepack package is part of package.json, yarn will take care of installing it so that it can compile the JavaScript code.

Including the JavaScript code in the app

We have seen how to compile the JavaScript code using webpacker but how to include it in our apps?

For that, Webpacker provides a helper method javascript_pack_tag. As the name suggests, we use it to include the webpacker packs in our layout files. This is equivalent to javascript_link_tag from the assets pipeline.

# app/views/layouts/application.html.erb

<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

The javascript_pack_tag takes care of making sure that it references compiled assets properly in development mode as well as in production mode similar to the assets pipeline.

That's all for today. We got an overview of how Webpacker enables us to use webpack in a Rails 6 application and we understood packs in the Webpacker world. We also saw how the compilation happens and how to use the compiled code in the application.

Next up, check out how to master the "packs" in Webpacker.

If you like this blog post, please consider supporting me on Patreon. It will help me in producing more useful Ruby/Rails related content on consistent basis 🙏