The import-maps gem finally allows us to get rid of JavaScript tooling in our Ruby on Rails projects, whether it’s esbuild or webpacker.

Let’s see together how to convert a project using esbuild, in my case, to import-maps.

Initial State

Our layout links the application.js and application.css files.

# views/layouts/application.html.erb
<head>
  <!-- ... -->
  <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
  <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
</head>

The CSS file imports styles from various NPM packages located in the node_modules directory as well as TailwindCSS directives.

@import "notyf/notyf.min";
@import "trix/dist/trix";
@import "actiontext.css";
@tailwind base;
@tailwind components;
@tailwind utilities;

And the JS file imports different packages as well as our Stimulus controllers.

import "@hotwired/turbo-rails";
import "trix";
import "@rails/actiontext";
import "./controllers";

A setup that couldn’t be more classic.

Installing import-map

If your app wasn’t created with Rails 7+, run the commands below to install the gem.

$ bundle add importmap-rails

Then:

$ bundle exec rails importmap:install

You will find the new importmap.rb file in the config directory, which somewhat replaces the package.json file that read our NPM dependencies.

There, you’ll find a list of pins.

Packages can be loaded from several sources:

  • A URL: pin "notyf", to: "https://ga.jspm.io/npm:notyf@3.10.0/notyf.es.js"
  • A gem: pin "@hotwired/turbo-rails", to: "turbo.min.js"
  • A local file: pin "application"
  • A local directory: pin_all_from "app/javascript/controllers", under: "controllers"

Update

The import-map installation has already prefilled the importmap.rb file for you.

You should find there the @hotwired/stimulus-loading pin, which is a new method of loading for Stimulus controllers that doesn’t require manually registering them.

You can now pin 🍆 your NPM packages to include them in the import list.

$ ./bin/importmap pin notyf

This command will automatically add all the dependencies of the notyf package.

Here’s the list of pins for me:

# config/importmap.rb
pin "application", preload: true
pin "i18n", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/utils", under: "utils"
pin_all_from "app/javascript/controllers", under: "controllers"
pin "trix"
pin "@rails/actioncable", to: "https://ga.jspm.io/npm:@rails/actioncable@7.1.1/app/assets/javascripts/actioncable.esm.js"
pin "@rails/actiontext", to: "actiontext.js"
pin "notyf", to: "https://ga.jspm.io/npm:notyf@3.10.0/notyf.es.js"

You can now modify your layout to load the import-maps.

# views/layouts/application.html.erb
<head>
  <!-- ... -->
  <%= stylesheet_link_tag "trix",
  <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>

  <%= javascript_importmap_tags %>
</head>

“We are now loading CSS files manually and one by one, to avoid having a single large bundle and to leverage the capabilities of HTTP 2 for loading multiple files quickly.

Cleanup

You can now delete all the files related to JavaScript tooling:

$ rm -rf package.json yarn.lock node_modules

Welcome to a rational world without all the complexity of frontend tools! 🥳

If you want to react to this article or need assistance with updating your Ruby on Rails applications, contact me on LinkedIn, Mastodon.”