⭐ This post is best experienced in my blog: https://ricostacruz.com/posts/javascript-in-rails-7
JavaScript in Rails 7 will be different: Webpacker is retired, and there are 2 new gems to manage frontend files.
-
🛌 Webpacker is retired — Webpacker 5 will be the last version, as announced on Jan 2022. The Webpacker 6 release candidate is now going to be community-maintained under a new name. (github.com: webpacker readme)
-
✨ New gem: jsbundling-rails — The
rails/jsbundling-rails
unifies support for any Node.js-based bundler (Webpack, Rollup, esbuild) under one gem. However, it only supports a subset of features that Webpacker does. (github.com: jsbundling-rails) -
✨ New gem: importmap-rails — The
rails/importmap-rails
allows using npm packages in the Rails asset pipeline. (github.com: importmap-rails)
With the deprecation of Webpacker, DHH recommends three possible alternatives:
-
Hotwire with import-map — The new Rails 7 default.
importmap-rails
will allow importing code from npm via a CDN (jspm.org). -
jsbundling-rails — New Rails 7 plugin for integration with Node.js-based bundlers (Webpack, Rollup, esbuild).
-
Shakapacker — A community-supported continuation of Webpacker maintained by Shakacode.
The importmap-rails
gem will come pre-loaded in Rails 7, and seems to be the answer to the question “how do I use npm packages in Rails?”.
-
How it works — Import-map is a lightweight tool to transform imports from npm module names (eg,
“react”
) into URL’s (eg, a CDN URL like"https://ga.jspm.io/npm:[email protected]/index.js"
). -
Importing URL's? — Yes, it’s natively supported by all modern browsers to import from URL’s like
import xyz from "https://..."
. (mozilla.org: import) -
Is npm/yarn required? — No. It can be used with npm, but the recommended experience appears to be to use the jspm.org CDN. (jspm.org: import maps explained)
// Transforms this:
import React from "react";
// Into this:
import React from "https://ga.jspm.io/npm:[email protected]/index.js";
There is no “build” or “compile” step in importmap-rails. There’s no Babel, PostCSS, or anything that transforms JavaScript code.
-
What about JavaScript modules ("esm")? — While Babel has often been used to convert ESM modules into common JavaScript code, Rails with importmap doesn’t do this. Instead, it relies on browsers’s built-in ability. (mozilla.org: JavaScript modules)
-
Limited React support — Because there is no compilation step, there is no support for build-time JSX compilation (it would have to be done on runtime using something like htm).
-
Limited legacy browser support — import-map relies on
<script type="module">
which is not supported on IE11. (caniuse.com)
It doesn’t combine many JavaScript files into one, unlike bundlers like Webpack.
-
No bundling of files — Rather than serving all files under one
application.js
, import-map will load all files individually(!). -
No tree-shaking — Since npm packages are loaded as-is (and often from a CDN), the benefits of removing unused code ("tree-shaking") isn't available when using importmap-rails.
-
HTTP2/SPDY is needed — Well, not required, but HTTP2 multiplexing will be needed to make loading hundreds of JS files a practical solution. (codavel.com: HTTP2 Multiplexing explained)
# While most applications would often serve a bundle
# of JavaScript like this:
https://example.com/assets/application-1a2b3c4d.js
# import-map's approach would be to *not* bundle the
# files, and serve them individually instead:
https://example.com/assets/application-1a2b3c4d.js
https://example.com/assets/components/button-1a2b3c4d.js
https://example.com/assets/components/radio-1a2b3c4d.js
https://example.com/assets/components/dialog-1a2b3c4d.js
https://example.com/assets/components/carousel-1a2b3c4d.js
The gem jsbundling-rails appears born out of the desire to use Webpack alternatives in Rails.
-
Replacement for Webpacker — While Webpacker offered very tight integration with Webpack (even its configs are in Ruby and Yaml), jsbundling-rails is a more “lightweight” integration.
-
esbuild, Rollup, and Webpack — Apart from Webpack, there are other bundlers supported. esbuild is a very fast alternative written in Go, and Rollup is a popular alternative often used with libraries.
-
Migration from webpacker — Seems easy. The goal is to make webpack self-reliant (eg, work without webpacker) and that’s it. (github.com: jsbundling-rails migration guide)
While jsbundling-rails supports Webpack, not all features can be taken advantage of.
-
No code splitting support — Webpack’s ability to lazy-load certain parts of the bundle is not supported. (webpack.js.org: Code splitting)
-
No hot reloading — Be ready to press F5 a lot.
-
Shaka? — The work on Webpacker has been continued by ShakaCode and was named for their company.
-
Shakapacker vs Webpacker — The last Webpacker version is 5. Shakapacker is based off of the unreleased Webpacker 6.
Features of each solution:
import-map | jsbundling | shakapacker | |
---|---|---|---|
npm modules | Via CDN only (jspm.io) | ✅ Yes | ✅ Yes |
Code splitting | No | No | ✅ Yes |
Hot module reloading | No | No | ✅ Yes |
React JSX | No | ✅ Yes | ✅ Yes |
Delivery to the end user | Multiple files | Bundled | Bundled |
Tree-shaking optimisation | No | Yes | Yes |
Thank you for sharing @rstacruz 👍