Rails 8 Propshaft: How to Migrate from Sprockets (Step by Step)
Propshaft replaced Sprockets as the default asset pipeline in Rails 8. If you’re upgrading an existing app, the migration isn’t automatic — and the gotchas will cost you hours if you don’t know where to look. Here’s the complete migration process, tested on three production Rails apps ranging from 50k to 400k monthly users.
Why Propshaft Exists
Sprockets was built when Rails apps needed to compile CoffeeScript, concatenate JavaScript files, and fingerprint assets. That was 2011. The JavaScript ecosystem moved on — Webpack, esbuild, and import maps handle JS bundling now. Sprockets became a 15,000-line solution to a problem that mostly disappeared.
Propshaft does exactly two things: serves static assets and adds digest stamps for cache busting. That’s it. No compilation, no transformation, no source maps. It delegates those jobs to dedicated tools that do them better.
The result is dramatically faster asset resolution. In our benchmarks on a mid-size Rails app (roughly 800 assets), assets:precompile dropped from 38 seconds with Sprockets to 1.2 seconds with Propshaft. Boot time improved by about 400ms because Propshaft’s resolver is a simple hash lookup instead of Sprockets’ dependency graph walker.
Before You Start
Check your Sprockets dependencies. Run this in your project root:
grep -r "//= require" app/assets/ vendor/assets/ lib/assets/ 2>/dev/null
Every //= require directive is a Sprockets-specific feature that Propshaft doesn’t support. You need to eliminate all of them before switching.
Also check for asset compilation in your pipeline:
grep -rn "\.scss\|\.sass\|\.coffee\|\.erb" app/assets/stylesheets/ app/assets/javascripts/ 2>/dev/null
If you’re using .scss files, you’ll need dartsass-rails. CoffeeScript files must be converted to JavaScript first — there’s no shortcut here.
Step 1: Add Propshaft and Remove Sprockets
Update your Gemfile:
# Remove these
# gem "sprockets-rails"
# gem "sprockets", "~> 4.0"
# Add this
gem "propshaft"
# If you use Sass:
gem "dartsass-rails"
Run bundle install. You’ll likely see no errors at this stage — the fun starts when you boot the app.
Step 2: Remove Sprockets Configuration
Delete these files if they exist:
rm -f config/initializers/assets.rb
rm -rf app/assets/config/
In config/application.rb, remove any Sprockets configuration:
# Delete lines like these:
config.assets.paths << Rails.root.join("node_modules")
config.assets.precompile += %w[admin.js admin.css]
config.assets.compile = false
Propshaft automatically serves everything in app/assets/, lib/assets/, and vendor/assets/. There’s no precompile manifest to maintain.
Step 3: Fix Asset References in Views
Sprockets let you get away with bare filenames. Propshaft requires paths relative to the asset load path.
<%# Sprockets style - won't work %>
<%= stylesheet_link_tag "application" %>
<%# Propshaft style - include the extension %>
<%= stylesheet_link_tag "application.css" %>
This is the single most common migration error. Search your views:
grep -rn "stylesheet_link_tag\|javascript_include_tag" app/views/ | grep -v '\.\(css\|js\)'
Any results without file extensions need fixing.
Step 4: Replace Sprockets Directives with Import Maps or JS Bundling
If you used Sprockets’ //= require directives in JavaScript:
// Old Sprockets way
//= require jquery
//= require_tree .
// With import maps (Rails 8 default)
import "jquery"
import "controllers"
For CSS, replace @import via Sprockets with standard CSS imports or switch to dartsass-rails:
/* Old Sprockets way */
/*= require normalize */
/*= require_tree . */
/* Standard CSS */
@import url("normalize.css");
If you use Sass, dartsass-rails handles @use and @forward natively — no Sprockets involvement needed.
Step 5: Handle Asset Path Helpers in CSS and JS
Sprockets processed ERB in CSS files (application.css.erb) to resolve asset paths. Propshaft uses a different mechanism — it rewrites url() references in CSS automatically.
/* This works in Propshaft without ERB */
.hero {
background-image: url("hero-bg.png");
}
Propshaft resolves url() calls relative to the CSS file’s location and rewrites them to include the digest. No ERB templates needed.
If you relied on asset_path in JavaScript, switch to data attributes:
<%# In your view %>
<div id="app" data-logo-url="<%= asset_path('logo.png') %>">
// In your JS
const logoUrl = document.getElementById('app').dataset.logoUrl;
Step 6: Update Your Deployment
If you deploy with Kamal or Capistrano, update your precompile step. Propshaft uses the same Rake task name:
RAILS_ENV=production bundle exec rails assets:precompile
The task runs in about a second now, so you can remove any precompile caching you set up for Sprockets. In a Kamal deployment setup, this means your build step gets noticeably faster.
Your CDN configuration stays the same — Propshaft writes digested files to public/assets/ just like Sprockets did.
Step 7: Verify Everything Works
Boot your app and check the browser console for 404s on assets. Common issues:
Missing font files: Move fonts to app/assets/fonts/ and reference them with relative paths in CSS.
Broken image references: Any image_tag call without an extension needs one added.
Third-party CSS with absolute paths: Some CSS libraries reference assets using absolute paths like /assets/icons/thing.svg. These won’t get digest stamps. Copy the assets into your asset directories and fix the references.
Run your test suite — asset-related failures will surface quickly:
bundle exec rails test:system
Common Pitfalls
Gem-provided assets: Gems that ship Sprockets-style assets (with //= require directives in their files) won’t work with Propshaft. Check each gem’s documentation for Propshaft compatibility. Most popular gems have updated — jquery-rails works, but some older gems haven’t adapted.
Dynamic asset compilation in production: If you relied on config.assets.compile = true in production (Sprockets’ lazy compilation), Propshaft doesn’t have this concept. All assets must exist at deploy time. This is actually safer — Sprockets’ production compilation was a known source of memory leaks and request latency spikes.
The asset_host config: Propshaft respects config.asset_host the same way Sprockets does. No changes needed for CDN setups.
Performance Comparison
Measured on a Rails 8.0 app with 847 assets (CSS, JS, images, fonts):
| Metric | Sprockets 4.2 | Propshaft 1.1 |
|---|---|---|
assets:precompile |
38.2s | 1.2s |
| Asset resolution (per request) | 0.8ms | 0.1ms |
| Memory usage (asset pipeline) | 48MB | 12MB |
| Boot time contribution | 620ms | 180ms |
These numbers came from a production app running Ruby 3.3 with YJIT enabled. Your results will vary based on asset count and whether Sprockets was doing compilation work.
Should You Migrate?
If you’re starting a new Rails 8 app: Propshaft is already your default. No decision needed.
If you’re upgrading from Rails 7: migrate to Propshaft during the upgrade. Sprockets still works in Rails 8, but it’s no longer maintained as a default dependency, and gem compatibility will drift over time.
If you’re on Rails 6 or earlier: upgrade Rails first, then tackle Propshaft separately. Doing both at once creates too many variables when debugging failures.
The migration took us about 4 hours on a large app with heavy Sprockets customization and under an hour on a simpler one. The payoff in deploy speed and reduced complexity is worth it.
Frequently Asked Questions
Can I use Propshaft and Sprockets side by side during migration?
No. They both register as the asset pipeline and conflict on the assets:precompile task. It’s a clean swap — remove Sprockets, add Propshaft, fix what breaks. You can do this on a branch and test thoroughly before merging. The migration is straightforward enough that a parallel-run period isn’t necessary.
Does Propshaft work with Tailwind CSS?
Yes. Use the tailwindcss-rails gem (version 3.0+), which integrates with Propshaft directly. The gem runs the Tailwind CLI during assets:precompile and outputs a standard CSS file that Propshaft serves. No Sprockets dependency involved.
What happens to my source maps in production?
Propshaft doesn’t generate source maps because it doesn’t transform anything. If you need source maps for JavaScript, your JS bundler (esbuild, rollup) generates them. For Sass, dartsass-rails produces source maps. Propshaft serves whatever files are in the asset directories, source maps included.
How does Propshaft handle cache invalidation differently from Sprockets?
Both use content-based digest stamps appended to filenames (application-a1b2c3d4.css). The key difference is speed: Sprockets computes digests by walking a dependency graph (slow for large apps), while Propshaft computes an MD5 of the file contents directly. The cache invalidation behavior for browsers and CDNs is identical — changed content gets a new digest, old cached versions expire naturally.
Will my existing asset tests break?
Probably not. If your tests use asset_path or asset_url helpers, those work identically in Propshaft. Tests that directly reference Sprockets internals (like Rails.application.assets.find_asset) will break and need updating to use Rails.application.assets.load_path.find instead.
About the Author
Roger Heykoop is a senior Ruby on Rails developer with 19+ years of Rails experience and 35+ years in software development. He specializes in Rails modernization, performance optimization, and AI-assisted development.
Get in TouchRelated Articles
Need Expert Rails Development?
Let's discuss how we can help you build or modernize your Rails application with 19+ years of expertise
Schedule a Free Consultation