It is a major version up, in particular:
- Rails 6 almost dropped Sprockets in favour of Webpack
- Rails 7 almost dropped Webpack in favour of jsbundling-rails + import maps (or esbuild) + Sprockets.
The backgrond is
- Webpack packs all JavaScript into a single file when called.
- Sprockets gets in the way; cf., Webpacker vs Sprockets : the battle is over (written when Rails 6 became the standard)
- It is cumbersome and slow.
- Modern web browsers can separate small JS files, not requiring such single large JavaScript
- Hence, Rails-default asset-generator Sprockets does a good job, separating JS and Ruby parts nicely?
In short, in Rails 7, you can even ditch Node/npm/yarn in addition to webpack, providing you use importmaps, and it seems that is the way forward in the mind of the Rails core team.
However, not all the projects can straightaway follow the path, because some bundling/packing mechanism is required for some JavaScript modules. If that is the case, you perhaps want to migrate from Webpack to Esbuild, which is much lighter and faster and is seemingly more favoured (hence better supported?) in Rails 7, though it is perfectly possible to keep using Webpack in Rails 7.
Perhaps because of that, the default new Rails setup with Bootstrap includes Esbuild:
% bundle exec rails new . --css=bootstrap
In this article, I summarise how to migrate
- from Rails-6 + Webpacker + Bootstrap + jQuery
- to Rails-7 + Esbuild + Bootstrap + jQuery
on the basis of my limited experience.
For how to use importmaps
with Bootstrap, refer to "Simple way how to use Bootstrap 5 in Rails 7 - importmaps & sprockets" by BootrAils and "Rails 7, Bootstrap 5 and importmaps without nodejs". In short,
bin/rails importmap:install
bin/importmap pin bootstrap
is the way, seemingly. And there will be even no package.json
Suppose you keep using Node/npm/yarn in your Rails-6 project but ditch webpack
, which is in practice replaced with esbuild
.
Here is the way for it.
Basically, Webpack uses the slightly non-standard-sounding directory /app/javascript/packs/
as the default directory. Esbuild uses /app/javascript/
for it. All JS files must be moved, accordingly.
Also, turbolinks
JS is replaced with turbo
(they have similar names but are different).
In Webpack, you can include JS files anywhere in an HTML file. However, in Esbuild, all JS files are strongly recommended to be placed in the HTML <head>
. You may need be careful about this point.
So far, I do not know how to selectively load JS files, depending on HTMLs. In default, all JS files are read in any pages.
As for jQuery, here I use jquery-ui-dist
to use all jQuery-UI functions (you need yarn install
it). You may import only your preferred UI functions instead (see below).
First, you should upgrade Rails from Versions 6.1 to 7, following the standard way according to the official doc "Upgrading Ruby on Rails".
-
In
Gemfile
, addgem 'jsbundling-rails'
-
would have been done in a default clean build with
bundle exec rails new . --css=bootstrap
-
It must contain
gem "sprockets-rails"
-
Replace gem
turbolinks
withturbo-rails
-
Replace gem
sass-rails
withgem "cssbundling-rails"
-
needs
sassc-rails
(even though the latter does not exist in default in Rails 7) -
gem 'listen', '~> 3'
seems necessary (even though not in default in Rails 7) -
needs
gem "stimulus-rails"
-
remove the line for
webpacker
orwebpack
-
In development/test environments, replace
gem 'byebug'
withgem "debug", platforms: %i[ mri mingw x64_mingw ]
(Ruby-3 default! Not applicable in Ruby 2, I believe.) -
In development, remove
gem 'spring'
andgem 'spring-watcher-listen'
-
gem 'rails-i18n', '~> 6.0'
should be upgraded togem 'rails-i18n', '~> 7.0.0'
-
In Ruby 3.1, you need the following (see https://stackoverflow.com/a/72474475/3577922); also you may need
gem 'matrix'
if you use matrixes.gem 'net-smtp', require: false gem 'net-imap', require: false gem 'net-pop', require: false
-
-
./bin/rails javascript:install:esbuild
-
This creates, among others,
app/assets/builds directory
,Procfile.dev
,bin/dev
-
would have been done in default with
bundle exec rails new . --css=bootstrap
-
package.json
should include:{ "scripts": { "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=assets", "build:css": "sass ./app/assets/stylesheets/application.bootstrap.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules" }}
-
-
Additionally, if you need a more complex JS bulid scenario, you can create
esbuild.config.js
(in the Rails top directory!) as:require('esbuild').build({ entryPoints: ['app/javascript/application.js'], bundle: true, sourcemap: true, watch: process.argv.includes("--watch"), outdir: 'app/assets/builds', }).catch(() => process.exit(1))
and replace the relevant line in
package.json
with"build": "node esbuild.config.js"
-
If you use ActiveStorage, you should edit
package.json
so it includes (I am not sure about ActionCable, though!):"dependencies": { "@rails/actioncable": "^7.0.4", "@rails/activestorage": "^7.0.4" }
-
Copy the contents of (the Webpacker entry point)
app/javascript/packs/application.js
to (esbuild entry point)app/javascript/application.js
- Make sure to (appropriately) replace
require
withimport
(see the reference above for how to) - For example, convert
require("channels")
intoimport "./channels"
; n.b., in this particular case, it did not work well and I ended up deleting the entire line, which does not cause any apparent problems.
- Make sure to (appropriately) replace
-
Edit the beginning of
app/javascript/packs/application.js
as//import "@hotwired/turbo-rails" // Rails 7 default import { Turbo } from "@hotwired/turbo-rails" Turbo.session.drive = false import "./controllers" import * as bootstrap from "bootstrap"
- Here, the first line (which is in defualt in Rails 7) is replaced with the second and third lines. Otherwise, JavaScript is not fired when a page is loaded for the first time but only after the page is reloaded. See Stackoverflow answer and also Turbo-Rails README in Github
-
Do a project wide search for all instances of
javascript_pack_tag
and remove them. -
Another key file is
config/webpack/environment.js
, where some settings are written. Transfer contents toapp/javascript/application.js
- Check out the directory
config/webpack/
and delete it.
- Check out the directory
-
Edit
app/assets/config/manifest.js
. Rails 7 version does not include://= link_directory ../stylesheets .css
but you may need it, in particular if you have your custom CSS in the directory. Also,activestorage
is optional.//= link_tree ../images //= link_directory ../stylesheets text/css //= link_tree ../builds //= require activestorage
-
If you have
app/assets/stylesheets/application.scss
, there will be a collision withapp/assets/builds/application.css
. So,(git) mv application.scss application_extra.scss
-
Specify its import in
/app/assets/stylesheets/application.bootstrap.scss
(providing you use Bootstrap):@import './application_extra.scss';
-
-
In
app/views/layouts/application.html.erb
, add<%= stylesheet_link_tag "application_extra", "data-turbo-track": "reload" %>
(after loading other default CSSs).- Accordingly, remove the related statements (2 lines) in
/app/javascript/application.js
- Accordingly, remove the related statements (2 lines) in
-
Run
yarn remove turbolinks
, which removesturbolinks
frompackage.json
-
Custom JS files in
/app/javascript/packs/
should be moved to/app/javascript/
and the former directory should be deleted. -
Run
yarn install
-
For
jQuery
, see "How to use jQuery & jQueryUI with Esbuild" (GoRails)-
yarn add jquery
-
Create
/app/javascript/add_jquery.js
with (for global setting); n.b., apparently, it is important to create a separate file for it and not write the sentences directly inapplication.js
because of hoisting of JavaScript (see GoRails or comment in Stackoverflow)import jquery from "jquery" window.jQuery = jquery window.$ = jquery
-
In
/app/javascript/application.js
, addimport "./add_jquery"
, followed byimport "jquery-ui-dist/jquery-ui"
(Reason: the latter must be read afterwindow
is defined. Make sure the path is correct! Note that you could alternatively download your own selection of jQuery functions, as explained in the GoRails instruction.) -
Because the global setting is done, the related setting in each JS file must be deleted.
-
-
You should try
bin/rails assets:clobber
(bin/rails assets:clean
is much less aggressive.) -
If
public/packs/
andpublic/packs-test
exist, delete them. (see Stackoverflow; though it may not be enough...) -
for
rails_admin
, delete the lineconfig.asset_source = :webpacker
in/config/initializers/rails_admin.rb
- Run
rails g rails_admin:install
so that it is set up for the Esbuild configuration, as opposed to the Webpack(er) configuration. Or, you may needDISABLE_SPRING=1 bin/rails g rails_admin:install
(see https://stackoverflow.com/a/72674116)
- Run
-
You may want to add a few lines in
.gitignore
(they may be added in default).- Compare your
.gitignore
with the one in a freshly created project.
- Compare your
- Migrate from webpacker to esbuild
- How to migrate from webpacker to jsbundling-rails (esbuild)
- "How we upgraded from Rails 6 to Rails 7" (BigBinary, 2022-09-20)
- including
redirect_to
(tighter security)
- including