Webpacker in Depth

Search icon
Search Book

What is Webpacker?

The goal of Webpack, or any front-end build system, is to allow you to write your front-end code in a way that is convenient for developers and then package that code in a way that is convenient for browsers.

As of Rails 6, Rails started to bundle and wrap Webpack inside the Rails applications. This is done through Webpacker.

Webpacker provides a pre-configured Webpack along with view helpers to easily get corresponding generated assets like JavaScript, CSS and SCSS files.

The Webpacker installation process calls the yarn package manager, then creates a package.json file with a basic set of packages listed, and uses Yarn to install these dependencies.

Along with that the following local files are created by Webpacker:

JavaScript Folderapp/javascriptA place for your front-end source
Webpacker Configurationconfig/webpacker.ymlConfigure the Webpacker gem
Babel Configurationbabel.config.jsConfiguration for the Babel JavaScript Compiler
PostCSS Configurationpostcss.config.jsConfiguration for the PostCSS CSS Post-Processor
Browserlist.browserslistrcBrowserlist manages target browsers configuration

JavaScript codebase in Rails

There is a app/javascript directory to host all the front-end related code, or more accurately all the JavaScript and related code.

This is how that directory would look like:

1   .
2   ├── channels
3   │  ├── consumer.js
4   │  └── index.js
5   ├── packs
6   │  └── application.js
7   ├── src
8   └── stylesheets
9      └── application.scss

The channels directory is generated by Action Cable component of Rails. It's not that significant for us at the moment.

The stylesheets and src directories are usually created by us, manually.

The packs directory

Typically, the file in the packs directory will be a manifest that mostly loads other files, but it can also have arbitrary JavaScript code.

It is important to note that only the Webpack entry files should be placed in the app/javascript/packs directory.

Webpack will create a separate dependency graph for each entry point, so a large number of packs will increase compilation overhead.

If you want to change the default packs directory path, you can adjust the source_path (default app/javascript) and source_entry_path (default packs) in the config/webpacker.yml file.

Within source files, import statements are resolved relative to the file doing the import, so import Bar from "./foo" finds a foo.js file in the same directory as the current file, while import Bar from "../src/foo" finds a file in a sibling directory named src.

Difference between Webpacker and Sprockets

Rails also ships with Sprockets, an asset-packaging tool whose features overlap with Webpacker. Both tools will compile your JavaScript into browser-friendly files and also minify and fingerprint them in production.

In particular, code can be added to Sprockets via a Ruby gem.

You should choose Webpacker over Sprockets on a new project if you want to use NPM packages and/or want access to the most current JavaScript features and tools.

For transitioning from Sprockets to Webpacker, we can make use of the following tools to achieve tasks:

Attach JavaScriptjavascript_include_tagjavascript_pack_tag
Attach CSSstylesheet_link_tagstylesheet_pack_tag
Link to an imageimage_urlimage_pack_tag
Link to an assetasset_urlasset_pack_tag
Require a script//= requireimport or require

Using CSS with Webpacker

Out of the box, Webpacker supports CSS and SCSS using the PostCSS processor.

To include CSS code in your packs, first include your CSS files in your top-level pack file as though it was a JavaScript file.

So if your CSS top-level manifest is in app/javascript/stylesheets/styles.scss, you can import it with import stylesheets/styles.

This tells Webpack to include your CSS file in the download.

To actually load it in the page, include <%= stylesheet_pack_tag "application" %> in the view, where the application is the same pack name that you were using.

Significance of stylesheet pack tag

We had replaced the stylesheet_link_tag with stylesheet_pack_tag in the application initialization chapter.

The reason is that in production environment, webpacker by default sets extract_css to true.

The way we tell Webpack which files to load is by using import or require statements. This includes CSS files, images, and everything else.

When we do import '../stylesheets/application.scss', we're telling Webpack to include application.scss in the build.

This does not mean it's going to be compiled into our JavaScript, but only that Webpack now knows that you want to compile this file.

How that file compilation is handled will depend upon how our loaders (css-loader, sass-loader, file-loader, etc) are configured.

When we do <%= stylesheet_pack_tag 'application' %>, that's a run-time inclusion from Rails, that has nothing to do with whether Webpack has built the stylesheets or not.

All that line is doing is saying "if you have a pack named application-*.css, include it here".

If Webpack doesn't build a separate pack of CSS, then this statement won't have anything to load, because no stylesheets were compiled.

Handling Webpacker config for each environment

Webpacker has three environments by default development, test, and production.

You can add additional environment configurations in the webpacker.yml file and set different defaults for each environment.

Webpacker will also load the file config/webpack/<environment>.js for additional environment setup.

Webpacker during deployment

Webpacker adds a webpacker:compile task to the assets:precompile rake task, so that any existing deploy pipeline that was using assets:precompile would work.

The compile task will compile the packs and place them in public/packs.

There might be times when we have to debug issues with asset compilation as it's done in production.

We can run the following command to simulate such a compilation locally:

1SECRET_KEY_BASE=`cat config/master.key.sample` RAILS_ENV=production RAILS_SERVE_STATIC_FILES=off bundle exec rails assets:precompile

Webpacker module and version issues

Node.js v16.5 doesn’t support lower versions of the node-sass package. We need at least node-sass version 6.xx.

But sometimes older @rails/webpacker package comes bundled with lower versions of node-sass.

Another issue is that the npm webpacker module's version is incremented very slowly and is very prone to breaking changes once released.

That's why we stick onto a specific Webpacker version in our application.

While creating the new Rails application if the @rails/webpacker package installed is not version 5.4.0 then please update the version manually to @rails/webpacker@5.4.0.

You can check default installed Webpacker version by running:

1yarn why @rails/webpacker

Downside of not running Webpack development server during development

When you don't have a Webpack development server running, then while trying to load the pack assets using Webpacker helper methods like javascript_pack_tag or image_tag, Rails will compile your entire assets on demand rather than upfront.

This can be noticed when conditionally rendering CSS with respect to the React component's state using the classnames library. In such a case, Webpack will boot from scratch and re-compile. It can make reloading the page extremely slow. For this reason it is recommended to use a development server during development.

Advantages of running Webpack development server during development

During the development, you will find yourself making changes to your JavaScript and CSS files very frequently.

Thus reloading after each change will be really slow and time consuming if you are not running Webpack development server. The reason is that by default Webpacker, tries to compile assets from scratch with each request.

One of the biggest upside of running the development server alongside the Rails server is that it will detect any changes to your JavaScript and CSS and re-compile it automatically, in real time without much delay.

Reloading manually before the development server has finished re-compiling, will only reload the same old assets. After compilation is done the browser window running your application will automatically try to fetch the latest assets.

Another benefit of running a Webpack development server along with the Rails server is that it makes catching errors specific to the backend and frontend easier.

When we don't run a Webpack development server, any errors regarding compilation of static assets are logged to the standard Rails log. This is really hard to debug since it's only a single terminal window showing all the errors.

Whereas, upon running a Webpack development server, all compilation errors or changes in static assets are reflected in only the terminal running the webpack-dev-server.

This will make debugging easier since the errors specific to the frontend side will be logged in the terminal running Webpack development server and the errors specific to Rails side will be logged in the terminal running Rails server.