iprog.com

Replacing the Rails 5.1 asset pipeline with webpacker 3

My last post was about using webpacker v3 on Rails 5.1.

This time I want to explore how to replace the traditional sprockets-based asset pipeline completely.

Our goal will be to add Bootstrap v4 and jQuery v3 to an app, handled entirely through webpacker. We won’t even install the sprockets related gems.

We’ll also enable rails-ujs, turbolinks, and even images using webpacker.

First steps

Let’s start with a new app:

1rails new look_ma_no_sprockets --skip-spring --skip-sprockets --webpack

Notice that Gemfile is rather sparse and leaves out all the sprockets related gems.

Next, create a controller, skipping the default assets that would be for sprockets:

1rails g controller home index --no-assets
2
3# We'll need these in a minute:
4mkdir -p app/javascript/stylesheets app/javascript/images

And make the new route the default in config/routes.rb:

1Rails.application.routes.draw do
2  root 'home#index'
3end

(See app at this stage)

Plumbing in webpacker

Now let’s adjust our layout file to use webpacker assets.

In app/views/layouts/application.html.erb, replace the default stylesheet and javascript tags with their webpacker equivalents:

1<head>
2  <title>LookMaNoSprockets</title>
3  <%= csrf_meta_tags %>
4
5  <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
6  <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
7</head>

We’ll be creating all stylesheets in app/javascript/stylesheets/. Yes, it’s a bit of an odd place, but it keeps everything for webpacker in app/javascript/, which is especially helpful if you decide to use both sprockets and webpacker.

Create app/javascript/stylesheets/application.scss:

1.webpacker-test {
2  color: blue;
3}

Add this line to the bottom of app/javascript/packs/application.js:

1require.context('../stylesheets/', true, /^\.\/[^_].*\.(css|scss|sass)$/i)

FYI, the true in the second argument is whether to search all subdirectories. We’ll leave it as true here, but know that you can restrict which directories are searched if you need to.

Let’s also replace app/views/home/index.html.erb with a few things to help us check our progress:

 1<h1>Home</h1>
 2
 3<div class="webpacker-test">
 4  I will be blue if webpacker's CSS is working.
 5</div>
 6
 7<div class="bootstrap-test text-success">
 8  I will be green if bootstrap is working.
 9</div>
10
11<div class="jquery-test">
12  jQuery is not yet working.
13</div>
14<script>
15  $(function(){
16    $(".jquery-test").html("jQuery is working.");
17  });
18</script>

Once this is in place, go ahead and fire up rails server and check your work at http://localhost:3000/. You should see the “blue” test pass, but not the other two (yet).

Your browser’s console log should also show “Hello World from Webpacker”. (If you don’t see it, make sure you’re showing all logs, not just errors.)

(See app at this stage)

Adding jQuery and Bootstrap

Now let’s get Bootstrap going. Since Bootstrap depends on jQuery and popper.js, we’ll handle those too.

As of this writing, Bootstrap 4 is at beta 2. Once v4 is fully released, you can drop the @version part off the end.

1yarn add jquery popper.js bootstrap@4.0.0-beta.2
2
3cp node_modules/bootstrap/scss/_variables.scss app/javascript/stylesheets/
4ruby -pi -e 'gsub(/ !default/, "")' app/javascript/stylesheets/_variables.scss

The last two lines above prepare a copy of Bootstrap’s variables for your app to customize. We won’t customize it here, but it’ll be ready for you later.

Next, add the following three lines to the top of app/javascript/stylesheets/application.scss. These will instruct our app to use Bootstrap’s CSS.

1@import '~bootstrap/scss/functions';
2@import 'variables';
3@import '~bootstrap/scss/bootstrap';

We also need to import jquery and bootstrap in our app/javascript/packs/application.js. Here’s the complete new file:

1import $ from 'jquery'
2global.$ = $
3global.jQuery = $
4
5import 'bootstrap'
6
7require.context('../stylesheets/', true, /^\.\/[^_].*\.(css|scss|sass)$/i)

Finally, we need to help jQuery and popper.js be seen by other .js files you’ll write. Replace config/webpack/environment.js with the following:

 1const { environment } = require('@rails/webpacker')
 2
 3const webpack = require('webpack')
 4environment.plugins.set(
 5  'Provide',
 6  new webpack.ProvidePlugin({
 7      $: 'jquery',
 8      jQuery: 'jquery',
 9      'window.jQuery': 'jquery',
10      Popper: ['popper.js', 'default'],
11  })
12)
13
14module.exports = environment

If you reload the page in your browser, you should now see some green text indicating Bootstrap’s CSS is working. The jQuery message should also have changed to show it is also working.

(See app at this stage)

rails-ujs and turbolinks

If you want to use rails-ujs (and you probably do, since Rails 5.1 now encourages remote/ajax forms), we now install it via yarn too.

1yarn add rails-ujs

Then, to app/javascript/packs/application.js, add:

1import Rails from 'rails-ujs'
2Rails.start()

(See app at this stage)

Adding turbolinks is similar.

1yarn add turbolinks

In app/javascript/packs/application.js, add:

1import Turbolinks from 'turbolinks'
2Turbolinks.start()

(See app at this stage)

Images

Images work a bit like stylesheets, in that they’ll be placed in app/javascript/images/.

Add this to app/javascript/packs/application.js, right after to the stylesheets line:

1require.context('../images/', true, /\.(gif|jpg|png|svg)$/i)

Using images (or other similar assets) within Rails views looks a little bit different from before. Use asset_pack_path() inside the main asset helper, like this:

1<%= image_tag(asset_pack_path('images/abc.svg')) %>

(See app at this stage)

Wrapping it up

That should be about everything. If you find a better way to do some part of this, please add a comment below.

For that matter, I’d love to hear how it goes for you.

tags: rails, webpacker, bootstrap

by Mark Oveson

To have the Rails object available generally, I think you need to add:

global.Rails = Rails;

to the application.js file.

Thanks for the nice article. Very helpful.

by tm

Yep, that should work great. Thanks for the helpful contribution!

by Robert Pierce

Thank you very much for putting this together. It was extremely helpful and the go-to guide while I was clean-slate restarting my app with webpack(er) instead of the asset-pipeline.

by tm

You’re very welcome. Glad it helped!

by Justin

You need to change set to prepend in the environment.js file for Rails 5.2

environment.plugins.prepend(
‘Provide’,
new webpack.ProvidePlugin({
$: ‘jquery’,
jQuery: ‘jquery’,
‘window.jQuery’: ‘jquery’,
Popper: [‘popper.js’, ‘default’],
})
)