With the introduction of the new asset pipeline, many new tools are available for you to use.
But if you want to know how to install Bootstrap in Ruby on Rails 7 and save some time building your apps, you've come to the right place.
There are at least two ways in which you can install Bootstrap.
Unfortunately, the second option (as you'll see in a minute) doesn't work out of the box.
If you want the video version of this post, here it is.
Option #1: New project
If you're starting from scratch, you're in luck because the way to install Bootstrap and Javascript bundling in a new Rails 7 application is super easy.
You can just use the -j esbuild --css bootstrap
flags, and you're done. And this approach works perfectly out of the box.
It'll install both the cssbundling-rails gem and the jsbundling-rails gem for you, generating the necessary configuration.
With this option you've got both the CSS and the Javascript part (using esbuild) working.
Option #2: Existing project
But if your project was started with import maps (the default in Rails 7), and you want to migrate to Bootstrap and a Javascript bundler (e.g., esbuild), well... the setup is not that straightforward.
The first thing you'll need to do is to install the cssbundling-rails
gem and then use the installer that the gem provides to generate the necessary configuration.
bundle add cssbundling-rails
./bin/rails css:install:bootstrap
Here's what the installer does.
- It creates the
builds
folder, and links it in the manifest file. - It removes the
application.css
file because it generates its own. - It adds a
package.json
file to store Javascript dependencies. - It installs the
foreman
gem, and it generates a config file for it. - It adds a
bin/dev
script to start your rails server, and watch for any changes to your CSS files. - It creates the Bootstrap-specific
scss
file, which will be bundled into anapplication.css
file. - It installs all the Javascript dependencies listed in the
package.json
file. - It appends the Bootstrap font path to the assets paths.
- It adds the Bootstrap Javascript import to the
application.js
file. - And finally, it configures the
build:css
command and runs it to build theapplication.css
file.
So if we try to use a Bootstrap component, like the Navbar, you'll see it looks pretty good.
But there's a problem. The drop-down doesn't work.
And that's because we don't have Javascript bundling set up yet.
So let's install the jsbundling-rails
gem, and add the esbuild
bundler by running the installer that the jsbundling-rails
gem provides.
bundle add jsbundling-rails
./bin/rails javascript:install:esbuild
Here's what the installer does:
- It checks for a
builds
folder, but because we already have it, it doesn't do anything. - It adds the
javascript_include_tag
to the application layout file. - It adds a task in the foreman config file, to watch for any Javascript changes.
- It installs the
esbuild
bundler, and it tries to build the Javascript code.
And that's where it gets into trouble.
$ esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets
✘ [ERROR] Could not resolve "controllers"
app/javascript/application.js:3:7:
3 │ import "controllers"
│ ~~~~~~~~~~~~~
╵ "./controllers"
Use the relative path "./controllers" to reference the file "app/javascript/controllers/index.js".
Without the leading "./", the path "controllers" is being interpreted as a package path instead.
1 error
node:child_process:866
throw err;
^
Error: Command failed: /Users/cezar/Work/ror/bootstrap/node_modules/esbuild-darwin-arm64/bin/esbuild app/javascript/application.js --bundle --sourcemap --outdir=app/assets/builds --public-path=assets
at checkExecSyncError (node:child_process:828:11)
at Object.execFileSync (node:child_process:863:15)
at Object.<anonymous> (/Users/cezar/Work/ror/bootstrap/node_modules/esbuild/bin/esbuild:209:28)
at Module._compile (node:internal/modules/cjs/loader:1099:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
at Module.load (node:internal/modules/cjs/loader:975:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
at node:internal/main/run_main_module:17:47 {
status: 1,
signal: null,
output: [ null, null, null ],
pid: 94418,
stdout: null,
stderr: null
}
Node.js v17.8.0
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Because we have some code left over from import maps, which conflicts with how the jsbundling-rails
gem works.
So let's fix these problems.
Fixing the installation
The first thing to do is to install the turbo-rails
and stimulus
packages.
yarn add @hotwired/turbo-rails
yarn add @hotwired/stimulus
Then, I'll adjust the import path in the application.js
file, and I'll remove the old stimulus imports.
--- a/app/javascript/application.js
+++ b/app/javascript/application.js
-import "controllers"
+import "./controllers";
--- a/app/javascript/controllers/index.js
+++ b/app/javascript/controllers/index.js
-import { application } from "controllers/application"
-
-// Eager load all controllers defined in the import map under controllers/**/*_controller
-import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
-eagerLoadControllersFrom("controllers", application)
-
-// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
-// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
-// lazyLoadControllersFrom("controllers", application)
+import { application } from "./application";
In the application layout file, I'll remove the javascript_importmap_tags
helper since it's no longer required.
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
- <%= javascript_importmap_tags %>
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
</head>
And finally, I'll unlink the other javascript folders leaving just the builds
folder and the images
folder in the manifest.
--- a/app/assets/config/manifest.js
+++ b/app/assets/config/manifest.js
//= link_tree ../images
-//= link_tree ../../javascript .js
-//= link_tree ../../../vendor/javascript .js
//= link_tree ../builds
So now, if we take a look at the Navbar in the browser, you'll see it looks the same but this time the drop-downs do work.
If you want to learn more about how the asset pipeline works, you can check out the Quick and Easy Guide to the Asset Pipeline in Rails 7, where I share an overview of the different tools that the new asset pipeline provides.