Howto migrate from Webpacker to cssbundling-rails in Ruby on Rails for CSS

Reading time: 6 minutes

Using cssbundling-rails for CSS pipeline

A lot of Ruby on Rails applications created with Ruby on Rails version 5.x or 6.x or even upgraded from the previous versions are using webpacker as their primary asset pipeline. But now as webpacker has retired, it is time to move on. cssbundling-rails is or will be the most obvious step to go.

If you were using webpack(er) for CSS too as I did, I recommend moving your CSS pipeline to an alternative before moving your JavaScript setup to an alternative like jsbundling-rails or importmap-rails.

Why move at all?

As previously said, webpacker was sunset and development already stopped 2021. Also, webpacker has a lot of old dependencies, especially webpack version 3 which can’t be upgraded.

Webpacker suffers a lot of problems that were never solved and now never would be solved at all. It was pretty hard to set up, but delivered quite a lot of useful and needed features back in the days when only sprockets were the only option for handling assets.

Prepare your project

First of all, you have to upgrade your project to at least rails 6.1.x (better rails 7.0.x) as it has everything prepared to get your project on cssbundling-rails. According to Gemfile from cssbundling-rails Ruby on Rails 6.1.0 is the minimum version.

I’ve tried to get all of my projects to current rails (current is 7.0.x) as priority one before I tackle such issues. But this is your decision. Ruby on Rails 6.1.X is enough for this step.

First move to cssbundling-rails before starting jsbundling-rails

As first steop move your css setup to cssbundling-rails and then start moving your JavaScript to jsbundling-rails. If you are a looking for a tutorial to move your project to jsbundling-rails I’ve written one too:

Install cssbundling-rails gem

Install cssbundling-rails first by running:

./bin/bundle add cssbundling-rails

You can also add it manually to your Gemfile and run bundle install to install it.

Select and run the setup you are using

cssbundling-rails has a lot of different install options you can use:

To make it as simple as possible, just use the setup you had previously used within your project. The first step is always to get your previous setup working with the new system.

Check which one is yours:

./bin/rails css:install:[tailwind|bootstrap|bulma|postcss|sass]

As a lot of my projects are using bootstrap, I’ll show here the bootstrap option as an example.

Using Bootstrap default setup with cssbundling-rails

I use the bootstrap option to show how the default setup is done using cssbundling-rails. You can move it to postcss or sass later. Just use the default options to get familiar with it and has a running project as soon as possible.

./bin/rails css:install:bootstrap

Follow the setup assistant. As with most existing (legacy) projects you may encounter more or fewer changes and errors you need to fix first.

Starting in development through bin/dev

If you were starting your project manually or from a script not named bin/dev, you may align it to the new rails default.

Your Procfile.dev should look something like this in diff:

diff --git a/Procfile.dev b/Procfile.dev
index 813d8e3..f418999 100644
--- a/Procfile.dev
+++ b/Procfile.dev
@@ -1,2 +1,3 @@
 web: bin/rails server -p $PORT
 webpack: bin/webpack-dev-server
+css: yarn build:css --watch

You can start your project by running bin/dev which should look like this:

#!/usr/bin/env sh

if ! gem list foreman -i --silent; then
  echo "Installing foreman..."
  gem install foreman
fi

exec foreman start -f Procfile.dev "$@"

If you could start the project and it was working, then you are like but most people won’t.

Downgrading bootstrap to the used version

Not all projects will be on the newest Bootstrap version (currently 5.2.3). You have to align package.json to your previous setup:

diff --git a/package.json b/package.json
...
   "dependencies": {
     "@rails/ujs": "^7.0.0",
     "@rails/webpacker": "^5.2.1",
     "bootstrap": "^4.6.0",
+    "bootstrap-icons": "^1.10.3",
     "jquery": "^3.6.0",
     "popper.js": "^1.16.1",
+    "sass": "^1.58.0",
     "stimulus": "^3.0.0",
   },
...

Now run yarn install to have the right dependencies installed.

Installing bootstrap-icons is optional. If you don’t use them, just remove the dependency. You can add it if you want to use them.

Move your CSS setup from webpacker to cssbundling-rails

Dependent on your setup, you may have more or fewer customizations on your Bootstrap setup.

As the first step I removed all CSS-related imports from app/javascript/packs/application.js:

diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js

// Import CSS
-import 'css/application'
+// import 'css/application'

As next step use the new CSS file generated by cssbundling-rails:

diff --git app/views/layouts/application.html.erb app/views/layouts/application.html.erb

-<%= stylesheet_pack_tag 'application', media: 'all',
-    'data-turbolinks-track': 'reload' %>
+<%= stylesheet_link_tag 'application', media: 'all',
+   'data-turbolinks-track': 'reload', defer: true %>

HINT: cssbundling-rails is loading app/assets/stylesheets/application.bootstrap.scss but you have to link the output file which is named application.css. That’s why you have to link to the application.

Update scss/sass file to your preferred name

You can rename the main CSS file to something else. I prefer to use a default application.scss and include the relevant files like a custom Bootstrap file, which imports only relevant parts of Bootstrap.

There are only 2 steps to have it as you want:

diff --git a/package.json b/package.json

   "scripts": {
-    "build:css": "sass ./app/assets/stylesheets/application.bootstrap.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules"
+    "build:css": "sass ./app/assets/stylesheets/application.scss:./app/assets/builds/application.css --no-source-map --load-path=node_modules"
   }

You can even use a sass file instead of scss if you want. The change should be obvious by renaming your application.scss file to application.sass.

Move your scss and css files to the new location

As all my css files were located in app/javascript/css/ folder. I moved them to app/assets/stylesheets.

Including them is as simple as it was before. There are view things you have to consider.

Before you just added the name of the file in the current directory and webpack did the work to find the right one. Now you have to tell sass, that the file is in the current directory:

dif --git app/javascript/packs/application.scss app/assets/stylesheets/application.scss

-@include 'custom';
+@include './custom';

If you have included something from node_modules, it mostly will stay the same. Previously you had to tell, that it is a .css file. Now you set the path to the file without giving .css or .scss at the end:

dif --git app/javascript/packs/application.scss app/assets/stylesheets/application.scss

-@import 'tom-select/dist/css/tom-select.bootstrap5.css';
+@import 'tom-select/dist/css/tom-select.bootstrap5';

If you have added only relevant CSS files from Bootstrap in your project, the imports look now a little bit different (without ~):

diff --git app/javascript/css/bootstrap_custom.scss app/assets/stylesheets/bootstrap_custom.scss

-@import '~bootstrap/scss/_breadcrumb';
+@import 'bootstrap/scss/_breadcrumb';

Updating your deployment system

As you are now using webpacker and sprockets-rails in parallel, you need to trigger both systems on deployment.

GitHub Actions (CI)

For GitHub Actions as CI system the change is pretty easy. You just have to add the sprockets command:

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml


-      - name: Webpack(er) compilation
-        run: NODE_ENV=test bundle exec rails webpacker:compile
+      - name: Assets and Webpack(er) compilation
+        run: |
+          bundle exec rails assets:precompile
+          NODE_ENV=test bundle exec rails webpacker:compile

Capistrano

The change for capistrano is pretty straight forward. Just enable assets compilation in your Capfile:

diff --git Capfile Capfile

-#require 'capistrano/rails/assets'
+require 'capistrano/rails/assets'
require 'capistrano/webpacker/precompile'

Summary

Moving from webpack(er) packed CSS setup to cssbundling-rails is pretty straightforward, if you know of small differences in using sass and webpack in development. I tried to tell here the most common things you may encounter.

The next step should be to move from webpacker to using jsbundling-rails with esbuild or webpack as JavaScript build system.

If you want to move your JavaScript to jsbundling-rails here is the next tutorial:

Happy coding!


Newsletter


See Also


Tags