In search of simpler JS stack in Rails apps

I'm not really happy with the current JS stack in Rails - importmaps-rails, jsbundling-rails, propshaft, turbo. Coming from Asset Pipeline it all feels too unnecessary complicated. Maybe it comes in handy for big apps with tons of JS but for normal apps i just don't know why.

But i like ESM modules and what they offer - better cacheing and composability.

Is it possible to use JS modules and dependencies with classic Asset Pipeline? This post will be mostly living reference for my projects to unify how to handle JS code.

1) Use Asset Pipeline


# Gemfile
gem "sprockets-rails"
gem "terser"

# app/assets/config/manifest.js
//= link_tree ../images
//= link_tree ../stylesheets .css
//= link_tree ../javascripts .js

Write scripts in /app/assets/javascripts. I name the files with suffix '.m.js'

Use Terser for JS minification.


# config/environments/production.rb
Rails.application.configure do
  config.assets.js_compressor = :terser
end

2) Use handwritten importmaps

In app layout it's perfectly possible to write importmaps by hand like that:


# app/views/layouts/application.html.erb
<script type="importmap">
  {
    "imports": {
    "application": "<%= asset_path('application.m.js') %>",
    "foo": "<%= asset_path('foo.m.js') %>"
  }
</script>

And also module scripts:


<script type=module>
  import 'application';
</script>

Module scripts can be of course linked and you can prepare some easy include helper for them

3) Dependencies from npm

If you need some external dependencies from NPM, use jspm.io and their online generator for importmaps with deps and copy and adjust your map in layout manually.

4) Don't use turbo or hotwire

Use rails-ujs

x) Sources