This lesson will go over the new jsbundling-rails gem and some of the pros and cons of using this gem.
The role of jsbundling-rails is to provide installers to set up esbuild, rollup, or webpack to allow bundling. After your bundler of choice is set up, your bundled output will use
app/assets/builds to hold the bundle output at which you can refer to it using the standard asset pipeline approach.
This section contains a general overview of topics that you will learn in this lesson.
- How to use a bundler with an import map based Rails app.
- Why you might use JS bundling over import maps.
- Some of the downsides of using JS Bundling.
- Why Webpacker was used for Rails 6.
- What the JS bundling options are.
Before we install the jsbundling-rails gem let’s go over a few of the options. The three options are:
Esbuild is a fast lightweight bundler written in Go. Whereas other bundlers may be slower, but have more features which can make the bundler take longer to run. Esbuild manages to be fast with variety of methods such as utilizing parallelism to use all available CPU cores.
If you’re interested in reading more about esbuild you can find the documentation here.
If you want to take a deeper dive into how to utilize Rollup you can find the documentation here.
The final bundler provided through jsbundling-rails is webpack which is a static module bundler that uses an entry point within your application to create a dependency graph that then combines every module your project needs into one or more bundles to be used by your application. If you want to take a deeper dive into webpack go here.
Now that you have been introduced into the bundlers provided through jsbundling-rails let’s go into setting it up.
Before we introduce utilizing jsbundling-rails let’s introduce a powerful new command with yarn.
yarn build --watch
When doing this you need to be running
rails server in another terminal as well. You will notice when you enter the command that the program will still be running and say
[watch] build finished, watching for changes.... That is the intended behavior.
Another useful command you can use is
./bin/dev which will start the
yarn build --watch and
rails server at the same time. That means not having to switch between terminal windows for developing. In development mode the command
First ensure that you have Yarn and Node installed. Run
yarn -v and
node -v in the terminal. Both of these should return a number indicating the version.
rails new testapp
Add the jsbundling-rails gem to your Gemfile.
Then run the command below in the terminal within your Rails application folder.
Remember the three options for the bundler are esbuild, rollup and webpack and you pick only one.
You should now see this error and a few other ones but don’t fear!
It’s quite explicit in the issue. Resolve is just a fancy word for saying the bundler wasn’t able to do anything with this import. Which makes sense as our testapp was set up initially with import maps instead of with esbuild or another bundler.
Now let’s see if we can resolve this in our
Now let’s run the command below.
yarn add @hotwired/turbo-rails
Next, run the build command.
yarn run build
The error is gone which means esbuild was able to resolve it since it was properly formatted. However that still leaves the
✘ [ERROR] Could not resolve "controllers".
Let’s see if we can solve that too! Remember what we said earlier about this error
✘ [ERROR] Could not resolve "@hotwired/turbo-rails". We need to download the appropriate package so that esbuild can bundle it. Go ahead and enter the command below!
yarn add stimulus
Now let’s run
yarn run build in the terminal. Woah! We still get the error below despite adding the stimulus package for esbuild. What gives?
Remember we ran this with import maps so the setup is different. Let’s go back into our
import "./controllers" to
Now when we run
yarn run build we should get the proper outcome! Now that we have walked through how to install a Rails app with import maps let’s make our life a little bit easier and set it up with jsbundling-rails!
Go ahead and enter the below command.
rails new myapp -j <replace the text and <> with your bundler choice>
Let’s walk through an example with esbuild. In the terminal entering this command will start the Rails app creation process.
rails new bundling -j esbuild
When running the build app you will see different output than the output shown in the Rails installation lesson. You should see something like this at the end.
info firstname.lastname@example.org: The CPU architecture "x64" is incompatible with this module. [3/4] Linking dependencies... [4/4] Building fresh packages... success Saved lockfile. success Saved 1 new dependency. info Direct dependencies └─ @email@example.com info All dependencies └─ @firstname.lastname@example.org
A key difference here is that within the terminal output you will see more output as well as that info output from esbuild as this command is ran. Let’s take a look at the results for our Rails application.
package.json file that is where our script build is kept. Here you will see the command that was run by Rails when building app. Going into the
An important caveat with esbuild is that in
index.js file. That command is
With that you will be able to add your controllers to the bundling process and use them. However make sure you run this command or else not all controllers will be included.
You may be wondering why we even need JS bundling when we have import maps. One of the most important things to remember is that this decision depends on the circumstances of your project and that while import maps eliminates the need for bundling and transpiling, you lose out on some of the benefits of using JS bundling.
Although HTTP 2 has reduced these costs, many JS packages still require explicit transpiling or bundling for performance or because of framework requirements. If you plan to use such frameworks for your project, it may be best to choose JS bundling over import maps.
It is important to remember that, in the days of HTTP 1, there was a performance penalty associated with making multiple requests. Since the introduction of HTTP 2, that performance penalty no longer applies. Therefore, one of the main benefits of using bundling, which was to mitigate the costs associated with HTTP 1, has become less significant.
Another downside to using JS bundling is that any changes to a module will expire the entire bundle, requiring the browser to redownload and parse everything from scratch.
In contrast, import maps keeps the modules separate, so you don’t have to redownload every single module anytime something changes. Just the kind of performance jump that may make you consider using import maps over JS bundling.
Oftentimes, the answer to these kinds of decisions is that there is no right answer. It all depends on what you’re looking for in your project. Want to use React with JSX and don’t mind a dip in performance? Consider JS bundling. Have some modules in mind you want to use that don’t require transpiling or bundling and want a boost in performance? Consider import maps.
One thing to really stress is that one is not necessarily worse than the other and it very much depends on your project needs. JS bundling gives you access to many frameworks such as React with JSX as well as certain features such as tree-shaking, whereas import maps does not require bundling and you don’t need to update every module when updating a module.
You may have heard of Webpacker or have come across it in the wild so it’s worth mentioning as it was used for quite a few years for Rails 6.
yarn add bootstrap
You would then be able to reference it within your pack file and now be able to use it!
While you may come across this in the wild it is important to note that Rails 7 does not use it any longer and replaces it with jsbundling-rails. If you want to use bundling make sure to use one of the bundlers listed earlier in the lesson.
This section contains helpful links to other content. It isn’t required, so consider it supplemental for if you need to dive deeper into something.
This section contains questions for you to check your understanding of this lesson. If you’re having trouble answering the questions below on your own, review the material above to find the answer.