Jul 16, 2026
Propshaft and the Rails Asset Pipeline After Sprockets
Rails 8 ships Propshaft as the default asset pipeline. It does dramatically less than Sprockets did — and that's the point. What changed, what migrating looks like, and where the edge cases bite.
For 12 years, Sprockets was the answer to “what compiles your assets in Rails?” In Rails 8, the answer is propshaft. The change is bigger than the gem swap suggests, because the philosophy changed: Propshaft doesn’t compile anything. It just serves.
What Sprockets did
Sprockets was an asset pipeline in the full sense. Given a manifest.js and a tree of source files, it would:
- Resolve
//= requiredirectives to assemble a single file from many - Run preprocessors (CoffeeScript, Sass, ERB-in-CSS, the works)
- Minify
- Fingerprint the output for cache-busting
- Serve the result, or precompile it to
public/assets/
It was the whole bundler. JavaScript, CSS, images — all of it went through Sprockets, and Sprockets understood every step.
This was great in 2012, when there was no other option. By 2020 it was a problem, because the JavaScript world had moved on. esbuild, Vite, and webpack were faster, smarter, and produced smaller bundles than Sprockets ever could. The Sass preprocessing, the CoffeeScript support, the manifest-based concatenation — all of it was either redundant or actively in the way.
What Propshaft does
Propshaft’s scope:
- Take a directory of static asset files (whatever produced them — esbuild, Sass CLI, Vite, you)
- Compute a digest of each file
- Serve them at fingerprinted URLs (
application-abc123.js) - Provide
asset_path/image_tag/stylesheet_link_taghelpers that resolve to the fingerprinted URL
That’s the whole gem. It’s serving and digesting. Compilation is not its job.
The implication: your bundler runs separately. In a typical Rails 8 app you’ll have:
bin/devrunning esbuild (or Vite, or importmap), which writes JS toapp/assets/builds/- The same script running
sass --watchortailwindcss -w, writing CSS toapp/assets/builds/ - Propshaft serving everything in
app/assets/builds/(andapp/assets/images/, etc.) at fingerprinted URLs
The boundary is clean. The bundler knows JavaScript. Propshaft knows HTTP. Neither pretends to know the other’s job.
The migration story
I haven’t migrated a 5-year-old Sprockets app to Propshaft, so take this with the salt it deserves. The migration guides on the Propshaft README lay out the moving parts, and they’re roughly:
- Replace Sprockets in your Gemfile:
gem "propshaft"instead ofgem "sprockets-rails". - Delete
app/assets/config/manifest.js. Propshaft doesn’t use it. It scansapp/assets/andvendor/assets/automatically. - Move JS preprocessing out. Anything Sprockets was doing for JS (concatenation, transpilation) needs to move to esbuild, Rollup, or importmap. The jsbundling-rails and importmap-rails gems exist for exactly this.
- Move CSS preprocessing out. If you were using Sass via Sprockets, you’ll want cssbundling-rails (which wraps Sass/Tailwind/etc.) or run Sass yourself in
bin/dev. - Delete the
//= requiredirectives. They mean nothing to Propshaft. Anything you want loaded should be in your bundler entry point. - Verify ERB-in-CSS hasn’t snuck in. Propshaft doesn’t run ERB on CSS files. If you have
<%= asset_path('logo.png') %>inside an SCSS file, you need to replace it with the CSS asset URL helper your bundler provides.
The last one is the trap. Sprockets supported <%= asset_path %> inside any file because it ran them through ERB. Propshaft doesn’t. If you grep your CSS and SCSS for <%= and find anything, that’s migration work.
What gets simpler
The bin/dev workflow. With Sprockets, the asset pipeline ran in-process inside the Rails server, which meant slow initial page loads in development as Sprockets compiled on demand. With Propshaft, the bundler runs as a separate watch process and the Rails server just serves the output. Hot reload feels closer to a JS framework’s experience.
The mental model. “Where is this asset compiled?” used to require knowing whether Sprockets, Webpacker, or Webpack was responsible — and the answer changed by file extension. Now: your bundler builds, Propshaft serves. One sentence.
The dependency tree. Sprockets pulled in a lot of transitive dependencies for preprocessors you probably weren’t using. Propshaft is small. The README says “minimal” and they mean it.
Where the edges bite
Sprockets-era engines. If a gem ships assets via Sprockets manifest directives (//= require), Propshaft won’t honor them. The asset files are still served — the gem’s CSS will still be at /assets/foo.css — but the implicit concatenation isn’t happening. Most gems have updated; check the gem’s CHANGELOG.
Source maps. Sprockets generated source maps for SCSS. Propshaft doesn’t preprocess, so source maps come from your bundler. Make sure esbuild (--sourcemap) or Sass (--source-map) is configured to emit them.
Image fingerprinting in CSS. Sprockets rewrote url() references in CSS to point to fingerprinted asset URLs. Propshaft does this too — there’s a CSS asset compiler that rewrites url(...) references to fingerprinted paths — but only if you’re using vanilla CSS or one of the supported preprocessors. If your CSS goes through Tailwind or another tool first, Tailwind’s output is what Propshaft sees, and it should rewrite url(...) in that output.
The Rails.application.assets API. Sprockets exposed an assets object you could query at runtime. Propshaft has a different (smaller) API surface. Code that introspects assets at runtime (rare, but it exists) needs updating.
The opinion
I’m bullish on Propshaft. It does one thing, the JS world has moved past needing Rails to bundle their code, and the result is a less surprising development experience.
The pushback I hear is “Sprockets worked fine for me.” That’s fair — if you have a Rails 7 app with importmap-rails handling JS and a Sass file or two, Sprockets was already doing approximately nothing for you. The migration to Propshaft will be approximately nothing of work.
The pushback I take more seriously is “I have a 10-year Sprockets app with 200 SCSS files, manifest directives, and ERB-in-CSS.” That migration is real work, and “stay on Sprockets” remains a defensible choice — sprockets-rails is still maintained, still on the Gemfile.lock of plenty of production apps. You don’t have to migrate on day one.
But if you’re starting a new app on Rails 8, Propshaft is the right default. The pipeline does less, and that’s the win.