Skip to content

Instantly share code, notes, and snippets.

@rstacruz
Last active January 11, 2024 06:33
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rstacruz/ea58ff0277b5e0b5938e86ea2d9571ba to your computer and use it in GitHub Desktop.
Save rstacruz/ea58ff0277b5e0b5938e86ea2d9571ba to your computer and use it in GitHub Desktop.

⭐ This post is best experienced in my blog: https://ricostacruz.com/posts/javascript-in-rails-7

JavaScript in Rails 7

JavaScript in Rails 7 will be different: Webpacker is retired, and there are 2 new gems to manage frontend files.

Situation

  • 🛌 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)

Three options

With the deprecation of Webpacker, DHH recommends three possible alternatives:

  1. Hotwire with import-map — The new Rails 7 default. importmap-rails will allow importing code from npm via a CDN (jspm.org).

  2. jsbundling-rails — New Rails 7 plugin for integration with Node.js-based bundlers (Webpack, Rollup, esbuild).

  3. Shakapacker — A community-supported continuation of Webpacker maintained by Shakacode.

npm support with import-map

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:react@17.0.1/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:react@17.0.1/index.js";

No compiling in importmap

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)

No bundling with import-map

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 jsbundling-rails gem

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)

Downsides of jsbundling-rails

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.

Shakapacker

  • 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.

Comparison table

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

Links

@truongduyng
Copy link

Thank you for sharing @rstacruz 👍

@zhouxiaozhen-github
Copy link

Thank you for sharing @rstacruz 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment