Skip to content

Migrate from importmap to esbuild for rails projects

Published: at 12:00 AM

Importmap is a great tool, but unfortunately it isn’t a silver bullet for everything. I decided to move away from importmap to esbuild for one of my projects because of it was so tricky to setup it to use with flowbite. Also, I’ll migrate from tailwindcss-rails to cssbundling-rails to make it consistent. Here’s a guide how to perform this migration.

Firstly, let’s update our Gemfile.

  gem "propshaft"
- gem "importmap-rails"
- gem "tailwindcss-rails"
+ gem "jsbundling-rails"
+ gem "cssbundling-rails"
  gem "turbo-rails"
  gem "stimulus-rails"

Next step - create package.json in the project root.

{
  "name": "app",
  "private": true,
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
  "devDependencies": {
    "esbuild": "^0.24.0"
  },
  "scripts": {
    "build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets",
    "build:css": "tailwindcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css --minify"
  },
  "dependencies": {
    "@hotwired/stimulus": "^3.2.2",
    "@hotwired/turbo-rails": "^8.0.12",
    "autoprefixer": "^10.4.20",
    "postcss": "^8.4.49",
    "tailwindcss": "^3.4.16"
  }
}

and of course we need node and yarn installed to run

yarn install

Don’t forget to add other libraries you use in your project which is referenced in your config.importmap.rb. After that, you can simply remove this file. Next stop - javascript files. Let’s update app/javascript/application.js:

- // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
+ // Entry point for the build script in your package.json
import "@hotwired/turbo-rails"
- import "controllers"
+ import "./controllers"

Next app/javascript/controllers/index.js

- // Import and register all your controllers from the importmap via controllers/**/*_controller
- import { application } from "controllers/application"
- import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
- eagerLoadControllersFrom("controllers", application)
+ // This file is auto-generated by ./bin/rails stimulus:manifest:update
+ // Run that command whenever you add a new controller or create them with
+ // ./bin/rails generate stimulus controllerName
+
+ import { application } from "./application"

If you’ve added stimulus controllers, you may need to run

./bin/rails stimulus:manifest:update

Let’s update layout app/views/layouts/application.html.erb

-    <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
-    <%= javascript_importmap_tags %>
     <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
+    <%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
+    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>

Our next step is to do some updates for stylesheets. You can remove app/assets/stylesheets/application.css, but don’t forget co copy your styles to app/assets/stylesheets/application.tailwind.css in case if you’ve added something. Then move config/tailwind.config.js to the project root. You may need to add missing tailwind plugins:

yarn add @tailwindcss/forms @tailwindcss/typography @tailwindcss/container-queries

Update Procfile.dev to add esbuild for JS and replace tailwind gem with node package with similar name :)

  web: bin/rails server
- css: bin/rails tailwindcss:watch
+ js: yarn build --watch
+ css: yarn build:css --watch

Well, looks like we’ve done. Let’s check our app with

bin/dev

Everything should work fine. You may also need to

But I believe you can do it without my help :)

If you like this post, you can hire me as an independent consultant for your project.
Just drop me a message using my contacts on the About me page