Guide to Rails 7 and the Asset Pipeline

Intro

Rails 7 brought with it an overhaul of the Asset Pipeline in the form of multiple new gems that either introduced a new way of handling assets (importmaps-rails), or broke down existing gems (propshaft, jsbundling-rails, cssbundling-rails) into smaller parts that make use of existing standalone tools and therefore support modern features that were previously unavailable.

While a few of these gems are still in pre-release status, their maintainers, contributors and early adopters have already deployed them to production. However, widespread adoption is currently being hampered by the lack of detailed documentation and some missing features that would allow them to support more use cases.

With the creation of the new Rails Discord channel for people interested in contributing to Rails, I think it’s a good time to coordinate with the community so we can pare down the rough edges. Therefore I’d like to start this thread so we can make a more visible demonstration that there are people working to improve things and provide central source of knowledge while all the related guides haven’t been updated.

If you want the TL:DR version of this guide, check “What will Rails recommend going forward?” further down.

This is a work in progress

Four approaches to the Asset Pipeline

  • Sprockets: The original asset pipeline gem, built for the HTTP/1 era and low javascript frontends. It handled the bundling and digesting of javascript, css and image files, without relying on node packages.

  • Webpacker/Shakapacker: Shipped with Rails 5.2, as a wrapper around the complexity of Webpack/Node/Yarn, this gem could completely replace Sprockets or simply take over javascript transpiling and bundling. It provided Rails “out of the box” support for SPA frameworks like React.

  • Import Maps: Shipped with Rails 7.0, it replaces Sprockets as the default asset pipeline gem. Although it eliminates the need for node/yarn and other complex tooling, it requires the application using it to be deployed in an environment that supports HTTP/2, otherwise it causes severe performance problems.

  • Bundling gems: Shipped with Rails 7.0, the multiple bundling gems provide a more traditional, if more modern, approach to the asset pipeline than import maps does. They basically break down the “all in one” approach of Sprockets into multiple smaller, specialized pieces. The main gems are propshaft, jsbundling and cssbundling.

Explanations and recommendations

I’m writing this not as maintainer of any of these gems, but as a regular contributor and someone who has deployed most of them in the monolithic production app that my own company relies on. Corrections and suggestions are welcome.

Sprockets

  • Status: Maintained

  • Notes: Several fixes and updates are only available in the edge version.

The original asset pipeline gem, Sprockets provides bundling and digesting for javascript, css and image files. Built before node was widely adopted, Sprocket’s “everything included” approach worked well for many years. However, as frontend programming became more complex, it started to fall behind dedicated tools like webpack. It was the only officially supported gem up to Rails 5.2, when Webpacker was introduced, and remained the default until Rails 7, when it was replaced by Import Maps.

If you are learning Rails: Don’t use it. Your time will be better spent learning one of the more modern approaches.

If you are starting a new app: Only if you absolutely do not want to deal with node/yarn and are not ready for import maps.

If you have an existing app with Sprockets: You can continue using it. Sprockets will receive maintenance updates at least until Propshaft reaches 1.0, and probably longer.

Webpacker

  • Status: Retired

  • Notes: All work done for the unreleased version 6 went to Shakapacker.

Rails has always had a complicated relationship with vanilla javascript, and to get around perceived limitations of the language, it adopted coffeescript as the default. Things changed with the arrival of ES6 and Babel for transpilation, but getting everything working so that you could use modern features of the language while remaining compatible with older browsers was difficult. Webpacker was created to solve that problem and for some time provided the bridge for developers interested in investing more heavily in the javascript ecosystem.

Five years later, all browsers oficially support ES6, so transpilation was no longer necessary and the extra complexity not worth for many developers. This lead to Webpacker being retired.

If you are learning Rails: Don’t use it. If your plan is to build your frontend entirely with Rails and Hotwired, then import maps is the preferred approach. However if your plan is to use a framework like React, then Shakapacker is the official sucessor and includes all the nice things that most javascript developers expect.

If you are starting a new app: Don’t use it. SPAs should go with Shakapacker, the official sucessor of Webpacker, which supports things like HMR. Hotwired/Tailwind based apps should go with importmaps-rails, or the bundling gems, which let you to choose your preferred bundling tool (esbuild, postcss, etc.).

If you have an existing app with Webpacker: Give some serious consideration to replacing it. If your app is a SPA, specially a React one, then Shakapacker is your friend, as it’s the official sucessor to Webpacker. If you are mostly using Hotwired and doing server side rendering, jsbundling-rails + webpack is an easy migration (and you can later migrate to the excellent esbuild).

Import maps

  • Status: Released, in active development

  • Notes: A shim is available to provide support for older browsers.

Rails 7 apps defaults to the importmaps-rails gem, which shares the same name as the import maps feature in browsers. This gem relies on the trifecta of HTTP2 (which eliminates the penalty of many small files), wide spread support for ES6 and the import map feature (which can the added to legacy browsers and Safari through a shim).

If you are learning Rails: This is the preferred approach, but only if you plan on building your frontends with Rails/Hotwired. If you want to use React/Vue/etc. then go with Shakapacker.

If you are starting a new app: Is it an SPA or your planned production environment only support HTTP1? This is not your friend, go with Shakapacker or the bundlings gems. However, if you are on the Hotwired team and have Cloudflare or Nginx in front of your Puma servers, then sure!

If you have an existing app: Stay where you are. This is a very different approach to handling javascript and css files and will require extensive work to migrate, specially if you have a lot of frontend code or rely on many node packages.

Propshaft

  • Status: Alpha, in active development

  • Notes: Already deployed to production in Basecamp (and by me, FWIW).

The sucessor to Sprockets, it has a much smaller scope than its predecessor: it’s only handles digesting, fixin references in css files so they use the digested filename, and moving everything to the public folder.

This means that any setup using propshaft will also be using one or more of the new bundlings gems, probably jsbundling-rails and cssbundling-rails.

If you are learning Rails: Don’t use it. It has no documentation, very few users and is still in alpha. You are already going to be spending a lot of effort learning other things, no need to make your life harder.

If you are starting a new app: Stick to import maps if you are going with Hotwired or shakapacker if you are going with React.

If you have an existing app: Maybe. It should be an easy migration for apps that are running Sprockets (CSS and digests) and Webpacker (javascript) and will also allow you to later webpack with the phenomenal esbuild. Just make sure you are comfortable running pre-release code with almost zero documentation in production. For what is worth, I’m using it in production and so is Basecamp.

jsbundling-rails

  • Status: Released, in active development

  • Notes: -

Provides javascript bundling features that were previously handled by Sprockets and Webpacker. Supports webpack, esbuild and rollup.

If you are learning Rails:

If you are starting a new app:

If you have an existing app:

cssbundling-rails

  • Status: Released, in active development

  • Notes:

Provides css bundling features that were previously handled by Sprockets and Webpacker. Supports Tailwind, Bootstrap, Bulma, PostCSS and Dart Sass.

If you are learning Rails:

If you are starting a new app:

If you have an existing app:

dartsass-rails

  • Status: Released, in active development

  • Notes:

Same as cssbundling-rails, but specific for dart. A wrapper around the platform specific versions of dart, instead of the yarn versions.

If you are learning Rails:

If you are starting a new app:

If you have an existing app:

What will Rails recommend going forward?

The default (no node/yarn):

  • Rails 7: Import Maps + Sprockets
  • Rails 8: Import Maps + Propshaft

The alternative way (with node/yarn and your choice of bundler):

  • Rails 7: Bundling Gems* + Sprockets
  • Rails 8: Bundling Gems* + Propshaft

* Bundling gems are cssbundling-rails, jsbundling-rails, etc.

Github Pages

Upgrade Guides

Relevant Information

Changelog

  • 2022-06-17: First version
  • 2022-06-18: Added the ‘Four approaches’ section. Rewrote some explanations
  • 2022-06-28: Added “What will Rails recommend”.
20 Likes

I’m looking for contributors to help with this guide so we can eventually turn it into an official Rails guide. If you’d like to contribute join us at the Rails Discord channel. If you have questions about the asset pipeline, post here.

4 Likes

Could this be turned into a draft PR? Would make it easier to share feedback inline.

Here it goes.

1 Like

I just need

1 Like

@brenogazzola thanks, will you put the rest of the text from your OP? I don’t see text e.g about jsbundling-rails, propshaft, etc.

Sprockets + jsbundling-rails + cssbundling-rails is my preferred setup and would recommend it for a new app if you need a front end that is too complex for import maps.

I migrated a large Rails app from Webpacker to that setup with Webpack and it is light years better in performance, maintainability, and reliability.

I would probably go use esbuild with the bundling gems in a new app.

1 Like

Hi, great guide.

Maybe it would be good to specify that whatever you use, you need Sprockets or Propshaft, unless you use Wekpacker or Shakapacker.

As explained now, it is understood that you have to choose between Sprockets, Propshaft, importmaps or jsbundling and cssbundling.

If you use importmap, you must use Sprockets or Propshaft.

If you use jsbundling or cssbundling or both at the same time, you must also use Sprockets or Propshaft.

If you use Webpacker or Shakapacker, you do not use Sprockets or Propshaft.

Anyway, now I see no reason to continue with Webpacker or Shakapacker, having jsbundling and cssbundling.

In my opinion, I would not recommend the use of unofficial or unmaintained gems, such as Webpacker or Shakapacker.

What do you think?

4 Likes

Interesting guide. Re-reading what I’ve written, I am not responding to this guide, per se, but to the choices made by Rails over the past few years when it comes to handling “assets”. (If I knew how to do ironic finger quotes, I would do them.)

I tried using webpacker several times over the last several years - particularly with new projects, but compared to using webpack it was a combination of “configuration: too hard”, “availability of webpack options: not good enough” and “directory naming: poor choices”.

Instead I use a hybrid approach: webpack/minipack** (not webpacker) for my work, and I let gems do their own thing, which is generally sprockets.

Now reading this guide I keep seeing “webpack: too hard”. I look at the multitude of options offered and which you should use depending on your capabilities and your type of application and it looks too hard. The hardest parts of configuring webpack have been conforming to Rails expectations of where javascripts, css, and images should live.

Import Maps looks as though it is the way to go, but then…

This [Import Maps] is a very different approach to handling javascript and css files and will require extensive work to migrate, specially if you have a lot of frontend code or rely on many node packages. … so perhaps not recommended.

Propshaft is still in alpha

…will also allow you to later webpack with the phenomenal esbuild …

There is a word missing here - I assume its “replace”.

All in all, looking at this it seems as if webpack and its successors continue to be a better path than the changing, changing, and changing again options that Rails has been offering.

I don’t have a well thought-out suggestion to offer, but I think if Rails stood back from taking decisions about frontend frameworks and let users pick the most suitable (popular/elite/easy) approach for a project, it would be better in the long run.

Anita

** Minipack, with minimal configuration, provides Rails helpers to handle webpack manifests.

1 Like

I’ve had some time to talk to others, and was offered a simplified explanation of what you are expected to choose:

The default (without node/yarn)

  • Rails 7: Import Maps + Sprockets
  • Rails 8: Import Maps + Propshaft

The “choose your own bundler” (with node/yarn)

  • Rails 7: Bundling Gems + Sprockets
  • Rails 8: Bundling Gems + Propshaft

I’ll also admit that the way I’ve written this guide makes it look more complicated, because I was trying to explain all the gems and give advice for three different types of users.

4 Likes

I wish we had this same guide for folks who are on Rails 6.

1 Like

Thanks a lot for all of this work @brenogazzola.

Should Upgrading Ruby on Rails — Ruby on Rails Guides should include part of this guide with only the mention “If you have an existing app” ?

I am wondering if guide should give an example with a basic tailwind, some custom code JS and CSS code and Stimulus (even a basic tree view in text). Especially on code organization. I see for example file migration on shakapacker migration guide.

Just stumbled across this and since it is certainly good for (new) users to expain the topic a bit i read. And i would be willing to help, having used rails for 15 years, especially in providing a bit of the other view or balance to some of the views expressed.

I think in general the focus here is too much on old/new , whereas it depends so much on what you are building, how much frontend/js do you have or need. And how do you want to achieve things.

Specifically sprockets, or maybe first the general concept off asset pipeline, was and is one of the main achievements of rails (along with migrations). I don’t think it is a good move to tell new users not even to learn that. I just read how vue calls it’s version 2 (now that they have 3): battle hardened. That is a good way to look at it. Sprockets helps in many ways, (and not everyone has http2), not just with images, but sass and many other transformations that can be nicely done. The asset concept is still very relevant to most i think.

Also i don’t see any conflict with importmaps. So i think the message should be start with Sprockets and then move to importmaps if you start downloading too much js packages. Which is sort of what comes at the end of the guide, while at the beginning sprockets is only if need be, old and not good kind of tool.

So, to summarize: Less focus on this learning/starting/existing , but instead focus on presenting the right tool for the job. Like tailwindcss is great for using tailwind without packers/node. css-bundling probably if you want to use postcss.

And don’t kill sprockets just yet. Rails has a long history of integrating alpha software into the stack (or leaning heavily towards basecamp usage), and i think a good guide should balance that, not encourage hopping on a wobbly wagon. (ie less propshaft)

Great guide! Really helped me understand the differences between the approaches. I’m beginning with RoR now, directly in v7 (also, beginning front-end development in general, since I’m a iOS developer I’ve got no knowledge in web front-end whatsoever), and spent the last week trying to figure the differences and pros and cons on the approaches, and this is the first detailed guide I’ve come to! Thanks!

But I’m stick with one doubt: You said on the sprockets part to don’t use it if you’re learning or beginning a new app, so I thought that only importmap or bundling gem alone would do all the job, that is, without sprockets. But, on the end, you said:

What will Rails recommend going forward?

The default (no node/yarn):

  • Rails 7: Import Maps + Sprockets

Now I’m confused. I suppose so that sprockets and importmaps don’t do the same thing. Is that right? Could I say that importmaps bundles external JS and CSS (e.g., bootstrap and it’s JS) into the project (I don’t know where) and sprockets packs (pipelines would be the right word?) it into 1 application.js and 1 application.css (or .scss) to deliver to my site, and that’s why they are both needed on the Rails 7 approach?

PS: One more thing: if I use importmaps, pinning Bootstrap to “bootstrap.min.js”, that is, not using CDN, as happens with the default CLI importmap pin bootstrap, do I also need to load bootstrap from the boostrap gem? If not, where do importmap saves the bootstrap css? Where does it come from in this case, not using CDN?

PPS: Sorry for any typos or mistakes, or beginners’ question. Greetings from Brazil! Please, feel free to correct me in anything that you see necessary, every little mistake, I’m really a beginner with rails.

Thanks in advance! :grinning:

2 Likes

The Rails pipeline executes multiple tasks:

  1. Transpilling files (coffescript to javascript, sass to css)
  2. Bundling files (combining all js files into a single application.js, same for css)
  3. Digesting files so they can be properly cached.

Sprockets was built to handle all 3 tasks. However with advances in bundlers like webpack, esbuild, dart, etc., Sprockets was left behind in terms of features and performance.

Because of that Rails 7 decided that tasks 1 and 2 should be replaced by importmaps (with importmaps-rails) or bundlers (with jsbundling-rails and cssbundling-rails).

So when I say “don’t use sprockets” I mean “don’t bother to use sprockets for tasks 1 and 2”. However, whatever approach you take, you will still need something to handle task 3. And those something are Sprockets and Propshaft.

PS: Sorry, can’t help you with that one. I don’t use importmaps, but the bundler gems, so I’m not certain how this should be done. I remember seeing guides for this though, so probably a google search for “rails importmaps bootstrap” will return a guide for this.

PPS: Beleza Pietro? Ta em qual cidade?

3 Likes

Opa, beleza? Sou de SP mas to morando em Guarapuava - PR. E vc?

Hey Folks! I’ve written a short tutorial on Medium about importmaps and the differences between the four ways of bundling assets. I hope you like it! Feel free to comment if I’ve said anything wrong.

2 Likes

Hey! I primarily write this to thank you guys for explaining all this, it was truly helpful to me.

A minor addition for me to understand it was looking up the official asset pipeline guide (The Asset Pipeline — Ruby on Rails Guides) to understand what task 3 is (Digesting files so they can be properly cached.). I copy it here:

Too much cache invalidation When static assets are deployed with each new release of code, the mtime (time of last modification) of all these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed.

Fingerprinting fixes these problems by avoiding query strings, and by ensuring that filenames are consistent based on their content.

Fingerprinting is enabled by default for both the development and production environments. You can enable or disable it in your configuration through the config.assets.digest option.

isn’t sprocket and importmaps by default in the Gemfile in a new rails app? reading this it seems you have to choose one or the other? I have sprocket and importmaps in the Gemfile. I make a custom dir under javascript and put a main.js file in there for my puny bits of js. pin it in importmaps.rb import it in application.js and done I think. and my main.css file is under stylesheets. Of course I am only learning and a total rails noob but for a start that works I think. Oh, and I skip action-mailermailboxcablehotewire too. And I use sqlite3 for production :slight_smile: there, I said it. but my sites are single user sites no visitors for now.

You can use sprockets by itself to perform every task of the asset pipeline (transpiling, bundling, fingerprinting, compression), or you can use it only for fingerprinting, rely on CDN for compression, and skip transpiling and bundling in favor of importmaps

2 Likes