Use the combined jsbundling-rails gem instead of individual js bundler gems


JSBundling provides a flexible way of using different Javascript bundlers like esbuild, rollup.js or Webpack.

It strikes a great balance between the asset pipeline and webpacker. jsbundling-rails is a much more lightweight integration.

This gem provides installers to get us going with the bundler of our choice in Rails application, and a convention to use app/assets/builds to hold our bundled output as artifacts.

The idea behind jsbundling is that we still use Yarn and the package.json file, but the built bundle goes into the asset pipeline download rather than being controlled by webpack.

Installation

We must already have node and yarn installed on our system. We will also need npx version 7.1.0 or later.

Then:

  1. Add jsbundling-rails to Gemfile with gem 'jsbundling-rails'

  2. Run ./bin/bundle install

  3. Run ./bin/rails javascript:install:esbuild (or replace esbuild with rollup or webpack)

Or, in Rails 7+, we can preconfigure our new application to use a specific bundler with rails new myapp -j [esbuild|rollup|webpack].

The installer creates an app/assets/build directory, appends that directory to the asset pipeline manifest at app/assets/config/manifest.js and .gitignore it.

The jsbundling:install command inserts a javascript_include_tag tag in our application.html.erb file.

This tag will include the new javascript entrypoint javascript/application.js for our build script to be included in our application.

Watch Mode

Run the bundler in watch mode in a terminal with yarn build --watch.

Start dev server with foreman start -f Procfile.dev or run rails s and yarn build --watch separately.

We can also use ./bin/dev, which will start both the Rails server and the JS build watcher.

Whenever the bundler detects changes to any of the JavaScript files in our project, it’ll bundle app/javascript/application.js into app/assets/builds/application.js.

Production

When we deploy our application to production, the javascript:build task attaches to the assets:precompile task to ensure that all our package dependencies from package.json are installed via yarn, and then runs yarn build to process all the entry points, as it would in development.

The latter files are then picked up by the asset pipeline, digested, and copied into public/assets, like any other asset pipeline file.

We can configure our bundler options in the build script in package.json or via the installer-generated rollup.config.js for rollup.js or webpack.config.json for Webpack.

One fantastic thing about jsbundling-rails is even if we decide to use Webpack instead of esbuild we can easily swap it out, and unlike Webpacker it will use a raw webpack config file.

That was a great decision because I imagine people who want to go all-in with Webpack will want to use their existing configs.

Comparison with webpacker

If we’re already using webpacker, we may be wondering how it compares to jsbundling-rails and whether we should migrate or stick with webpacker.

Here are some considerations:

  • jsbundling-rails is a much more lightweight integration. webpacker is more involved and opinionated.

  • jsbundling-rails can be used with multiple bundlers (currently esbuild, rollup, and webpack are supported out of the box, but anything that can put a bundle into app/assets/builds could be configured to work with it). webpacker is built specifically to integrate with webpack.

  • jsbundling-rails doesn’t tie us to specific versions of the JavaScript packages we use for bundling, transpiling, etc. webpacker releases are tied to a specific major version of webpack, babel, etc. This means we cannot freely upgrade those packages - we have to wait for a new webpacker major release that supports the newer versions, and upgrading to that new webpacker release requires upgrading to all of those new JavaScript package versions at once.

  • jsbundling-rails uses the standard configuration format for our bundler of choice. webpacker has its configuration layer on top of webpack’s configuration, which requires us to do some translation when following guides/documentation written directly for webpack.

  • jsbundling-rails works with the standard actionview asset helpers. webpacker provides the tag helpers that we need to use.

  • webpacker can be used as a complete replacement for sprockets, and in that setup, we can stop loading the sprockets/railtie in our application. jsbundling-rails (as well as css-bundling-rails) works in conjunction with sprockets.

  • webpacker supports using a dev server for hot reloading. jsbundling-rails hands-off static files to sprockets, so hot reloading is not supported (i.e., to load any JavaScript changes, we’ll need to do a local-state-clearing full page refresh).

  • webpacker delegates asset processing entirely to the external nodejs tooling. jsbundling-rails still relies on sprockets to output the final public assets and create the associated manifest. When the webpack has full control over the result, it can integrate additional features like automatic code-splitting of statically imported shared dependencies, and webpacker can load each entry point’s dependent chunks for us. With jsbundling-rails, we’ll be able to manually split out lazy-loaded chunks by using dynamic import()s.

To know more about this feature checkout this PR.

Join Our Newsletter