Gulp on Rails: Replacing the Asset Pipeline

Dan Tello, Former Senior Front-End Developer

Article Category: #Code

Posted on

Just over a year ago I started using Gulp and created gulp-starter. The setup works great out of the box on projects that are unopinionated about assets. Rails, unfortunately, is very opinionated, and it's taken me a year of trial and error to land on a solution that fully integrates a Gulp-based asset pipeline with Rails without compromising existing features or sacrificing speed, power, or flexiblity. The following setup is specifically customized for Rails with Heroku, but is easily adaptable to whatever tech stack you're using.

The Gulp Rails Pipeline

Better Stronger Faster

  • Leaves Sprockets and manifest files intact for use with gem installed assets
  • Transform and bundle CommonJS modules (js or coffee) with Browserify
  • Compile .sass/.scss with Libsass (node-sass through gulp-sass)
  • Autoprefix CSS
  • Optimize images (could be expanded to further proccess or resize images)
  • Compile an icon font + sass from a folder of SVGs
  • Full BrowserSync integration (the original Rails Asset Pipeline didn't work with live stylesheet injection)
  • Revision filenames in production for caching
  • Build assets on deploy with Heroku

File Structure

asset file structure

The ONLY thing in app/assets are manifest files. This lets us continue to use Sprockets to require gem assets if necessary (e.g., //=require jquery_ujs). The compiled files are .gitignore’d and rebuilt on deploy. All of our source assets (sass, js/coffee, images, svgs, etc.) exist in gulp/assets, and compile to public/assets. Check out the README for full details.

Why leave Sprockets in place?

In an early attempt to replace the Rails Pipeline (in a fit of frustration), I completely ripped it out. I disabled Sprockets, removed relevant gems, and replicated (and improved) every feature with Gulp tasks. It worked great for a while, until the development team we were handing the project off wanted to know how to include assets from a gem they'd installed. Normally, you'd just add a sprockets //=require some_asset to the relevant css or js manifest file. But I'd removed the Pipeline altogether, and manifest files weren't a thing anymore.

We had to backtrack and re-think the problem. My goal was to completely replace the pipeline with full feature parity, including support for gem installed assets. So, because some gems rely on the asset pipeline, removing it altogether was no longer an option. Instead, I needed figure out a way to work with it (that I didn't hate), and that's the solution detailed above.

Why bother at all?

If you're already a Gulp fan, skip this section and get started with the repo. If you are currently using the Rails Asset Pipeline, and are wondering why you should bother changing up your workflow, read on!

The Rails Asset Pipeline is dated. The build tools that have become available to us in the years since its inception are incredible, and the old pipeline has failed to evolve or keep up. JavaScript bundlers like Browserify and Webpack have brought modules to the browser and vastly improved how we get to write code. Libsass has pretty much caught up to Ruby Sass now and compiles RIDICULOUSLY faster (especially on larger projects)! Autoprefixer lets us stop worrying about vendor prefixes or mixin libraries like Compass or Bourbon to fill them in. BrowserSync gives us live reloading with refresh-less stylesheet and image injection across multiple devices and browsers at the same time, while syncing scroll and navigation between them, without the need for browser plugins or manually inserted script tags! Generating and using icon fonts or svg sprites has become as simple as dropping files in a folder with a couple of gulp tasks.

For me, there's no better tool than Gulp to glue these incredible build tools together. Unfortunately, Rails disagrees and reeeally wants us to use its tech stack to handle assets. Sure, there are a few gems out there that let you cobble together some of the technology above, but I've found that they quickly fall behind, don't expose all the same options, and some things just aren't possible.


I believe I've finally settled on an integration that feels right, and doesn't compromise any existing features or new workflows we're looking add. If you have ideas, suggestions, or questions, comment below or head on over to GitHub and start contributing!

Related Articles