The evolution of Rails Assets through time

What are assets?

  • Static images, css, js, fonts, videos, text files, binaries, etc
  • CSS and JS files change often
  • Images change less often
  • Videos and fonts don't change much

Before Rails 3.1

Before 2011

All assets in public folder or through CDNs

Issues with this approach:

  • Caching and cache invalidation (passing a "?timestamp" query string in the url)
  • Single large file vs multiple small files
  • No support for languages like CoffeeScript -2009- or SASS (TypeScript didn't exist yet)
  • Unoptimized file size
  • Hard to use existing libraries

The Assets Pipeline

With Sprockets - Since 2011

  • Introduced in Rails 3.1 with the `sprockets` gem as a dependency of Rails
  • For Rails 4.0, the integration between Sprockets and Rails was extracted as a new Rails dependency called `sprockets-rails`
  • Assets in /public were still valid

Solving problems:

  • Cache invalidation: a digest/fingerprint of the file content is appended to the file name
  • Bundling: replaces "require" directives with the actual code (a big long single file with all the code)
  • Transpilation: supports CoffeeScript and SASS/SCSS by default
  • Clean up files: uses compressors and uglifiers/minifiers to remove what a browser does not need and compress the file
  • Easy to incorporate assets from gems instead of copying them into the project
  • The `jquery-rails` gem is included as a default

Turbolinks (2013)

  • Included by default in new apps starting with Rails 4.0
  • Aims to provide a "single-page-app-like" feeling for multiple page applications
  • Basic functionality works fine, but many JavaScript packages where not fully compatible out of the box

Before Rails 5.1

Before 2016

  • The JS world was changing, but Sprockets kept adding features to be compatible with it
  • Support of importing assets from a node_modules folder (using Yarn)
  • Support for many of the new ECMAScript features (thought through some experimental flags)
  • Other solutions for the same problem appeared for other ecosystems, and the JS world changed more and more
  • Eventually, Webpack (2012) was the winner

Webpacker

  • Webpack was introduced as an option through the `webpacker` gem for new apps with the `--webpacker` flag in Rails 5.1
  • The `jquery-rails` dependency was removed in favor if the `rails-ujs` package that was part of Rails
  • `rails-ujs` was written in CoffeeScript, so `coffee-rails` was now a requirement to use that feature of Rails

The Dark Ages

2019 - 2021

  • The JS ecosystem was changing faster than what Sprockets could handle (new standards, new patterns, new languages, etc)
  • Webpack was capable of doing everything that was needed to support that, so it was made the default for JavaScript assets in Rails 6.0
  • The rest of the assets were still handled by Sprockets-rails by default
  • It was possible to configure Rails to handle all the assets using Webpacker too
  • Webpack cannot read assets from gems
  • Now gems that wanted to include assets had to also provide an NPM package (the gem for the Rails code and the NPM package for Webpack)
  • You could have both assets from Sprockets and Webpack for any of the asset types
  • Multiple ways of using a feature was confusing (for example, rails-ujs is included in Rails so it works OOTB if handling JS assets with Sprockets, but the `@rails/ujs` package is needed in the package.json file if using Webpack)

Where are we now?

Since 2021 (Rails 7.0)

  • Webpacker was retired and not a default anymore
  • importmap-rails, the default since Rails 7.0 (with support for Rails 6)
  • jsbundling-rails, allows using other popular bundlers and not just Webpack: esbuild, rollup, and bun
  • cssbundling-rails, allows using other popular CSS bundlers (tailwind, bulma, dart-sass, etc)
  • vite_rails, allows using Vite to handle assets (non-official)
  • Shakapacker, continuation of the Webpacker gem (non-official)
  • `sprockets-rails` is not a dependency of Rails anymore
  • `turbolinks` and `rails-ujs` are replaced for new apps in favor of `turbo-rails`
  • With the new defaults, new Rails 7.0 apps don't need Node to manage assets

The future

  • Propshaft to solve assets fingerprinting (default in Rails 8). It does way fewer things than Sprockets
  • Sprockets still supported ("for a long time" according to Propshaft's readme)
  • Native browser features
    • CSS vs SASS
    • JSDoc vs TypeScript
  • React compiler?

Summary JS

3.0 3.1 3.2 4.0 4.1 4.2 5.0 5.1 5.2 6.0 6.1 7.0 7.x 8-> Static assets (/public) sprockets-rails webpacker shakapacker importmap-rails jsbundling-rails
Supported option Rails default

Summary CSS

3.0 3.1 3.2 4.0 4.1 4.2 5.0 5.1 5.2 6.0 6.1 7.0 7.x 8-> Static assets (/public) sprockets-rails webpacker shakapacker cssbundling-rails propshaft
Supported option Rails default

Questions?

Resources: