Our goal: remove nodejs
As many other people in the Rails community, I started setting up brand new Rails 7 projects, and I need to re-learn, at least partially, how to bundle the assets and distribute them.
I never fell in love with TailwindCSS, and therefore I usually setup my Rails apps to use Bootstrap as default.
But what I really like about Rails 7, is the idea of being able to get rid of not only webpack, but of nodejs entirely. The new importmaps feature is really appealing to me and I'd like to use it as long as I don't need to bundle my javascript.
I have to say that esbuild
does already a pretty cool job compared to webpack
to simplify our lives, and make the process faster, but as long as I don't need bundling, I'd like to not have a package.json file and being dependent on nodejs for my Rails app.
A pure and simple sprockets + importmaps app with no Foreman, no bin/dev
, no yarn build --watch
stuff.
Bootstrap is made of two parts: CSS and javascript. So I want to use importmaps for the javascript part and rely on sprockets for the CSS compilation from SCSS.
Rails default
By default, Rails 7 provides a new option --css=bootstrap
,
but with my great surprise, this option adds both jsbundling-rails
, cssbundling-rails
, a package.json
and esbuild
.
Not as expected. Not what I want.
How to configure Rails and Bootstrap without nodejs
Default is not what I want, but I can still reach the goal and here I'll explain how:
Stick with just rails new myapp
This will setup exactly the tools I want: sprockets
and importmaps
. It will also setup automatically for me stimulus and turbo, which is great because I use them most of the time anyway.
Add bootstrap
gem and enable the gem sassc-rails
in the Gemfile. This will allow us to compile bootstrap from SCSS without node.
You can simply import Bootstrap styles in app/assets/stylesheets/application.scss
:
// here your custom bootstrap variables...
@import "bootstrap";
That's it for the CSS part. Running rails assets:precompile
will generate what you want.
For the javascript part we need to do three things:
- Precompile the bootstrap.min.js that comes with the gem, by adding to
config/initializers/assets.rb
Rails.application.config.assets.precompile += %w( bootstrap.min.js popper.js )
- pin the compiled asset in
config/importmap.rb
:
pin "popper", to: 'popper.js', preload: true
pin "bootstrap", to: 'bootstrap.min.js', preload: true
- Include bootstrap in your
app/javascript/application.js
:
import "popper"
import "bootstrap"
I prefer this approach rather than pinning a CDN because we avoid diverging versions of Bootstrap.
Conclusion
This is all you need to have Bootstrap fully working on Rails 7 without using node.
If you like this guide you can follow me on Twitter.
Top comments (35)
Great write up Alessandro. What I don't understand is if import maps loads libraries via CDN, why is the Bootstrap gem still needed? You could load Bootstrap straight into the header without all this hassle.
Hi Hashim,
yes, correct. You could load Bootstrap Javascript part through CDN, but you would still need the gem to use the SCSS stylesheets and customise it. Now, since you have the gem, I think is wise to use also the JS wrapped within the gem, so that you don't risk having divergent versions (one from CDN and one from the Gem).
Of course, if you don't need to customise bootstrap, then you can load everything from CDN.
Excellent and helpful article. Thanks Alessandro
Tried the whole day to get Bootstrap working with Rails 7.
This method works awesome.
Thank you. Clears up a lot of confusion. The dependancies and/or defaults of the Rails 7 options aren't clear.
Another confusion for me. Is Sprockets the way forward or?
Probably would help if you showed all your changed or added code. Would help avoid the confusion @ochupa faced.
Don't think this really "works without node" - bootstrap gem does not work if there is no execjs runtime (just try removing executable permission from your NodeJS installation on the system) because autoprefixer needs it. Is there some magic way to force it to use something else?
Good point! Yes, you can also use another runtime: github.com/ai/autoprefixer-rails#u...
I am also running into this problem with execjs.
I was hoping to set things up so that node is only needed in development, but not in production.
I would then have a two-stage Dockerfile where the assets precompile run is done with node present, but then node should not be needed.
Unfortunately, the execjs error about missing node prevents me from opening a rails console or running db:migrate.
I haven't managed to make this work using groups or require: false in my Gemfile.
Thanks bro.
Hi, I tried this but the CSS is not working. I get this error from the browser console:
seems like the @import "bootstrap" call inside application.css is importing the bootstrap javascript, because in Style editor of Firefox, I see a bootstrap file that is just javascript
ok, seems like I needed to rename "application.css" to "application.scss"
Hey!
I think this could be made simpler.
I believe the bootstrap-rubygem already injects in the assets throughs sprockets the
bootstrap.min.js
and SCSS files, and also thepoper.js
through the dependency popper_js-rubygem.So, in terms of the JS files, all we need to do, after bundeling the bootstrap-rubygem, is import them in the
application.js
I have this working in development.
Thanks. This is simpler.
In application.js :
give this error for me on browser :
full error log:
I miss the
bundle add bootstrap
This is a pretty neat solution, all work is done by Sprockets as intended. No Node, no Foreman, great.
However, enabling
sassc-rails
in Gemfile brings in embeddedsassc
, based on now deprecatedlibsass
v3.5 which does not handle modern Sass. So no@use
directives, no color functions likecolor.adjust
, etc., and one gets errors like:SassC::SyntaxError: Error: Function hsl is missing argument $saturation.
(that is if color variables are defined in modern format — without commas, e.g.$body-text-color: hsl(0deg 0% 20%)
).Is there a way to integrate Dart Sass into Sprockets or I am missing something?