Replacing Rails Asset Pipeline with Webpacker
This blog post was originally published a little while ago. Please consider that it may no longer be relevant or even accurate.
I'll admit that I'm still relatively new to Rails Webpacker, but on a new project I've decided to completely replace the Sprockets asset pipeline and instead handle all my assets through Webpacker. As a fellow convention over configuration fan I've tried my best to interpret what the convention for structuring a Webpacker app is - I think it's still early days and even the Rails team haven't worked it out yet. I suspect it's something the community will nut out over time. Nonetheless, here is how I've replaced the asset pipeline in my app.
If you're starting fresh, run rails new blank --skip-sprockets --webpack
to kick off a new Rails app without Sprockets and with Webpacker. If you need to add Webpacker to an existing project then read up on the docs. Then remove some gems from your 'Gemfile' you're not going to need - sass-rails
, uglifier
& coffee-rails
. You can also ditch app/assets
as you're not going to be using that anymore.
First, let's take a look at the default application.js
generated by Webpacker.
If you read the comment it's pretty clear that the app/javascript/packs
directory is meant only to be the entry point to your packs, and to put your actual app in app/javascript
. This leads us onto the directory structure.
Directory structure
This is how I've organised my Webpacker app - for an example app called blog
. You'll see that I've nested my actual JavaScript app in app/javascript/blog
rather than just app/javascript
and there's a reason for this. First, it means I can have multiple apps in the same project if I need them (rather than having all their code side-by-side). Second, It allows me to have a really pack entry point which I'll show next.
Now let's take a look at my app/javascript/packs/application.js
file - the entry point to my pack. It's stupidly simple:
This will go import and run app/javascript/blog/index.js
which will be the entry point to the rest of my JavaScript application. This keeps the package entry point as simple as possible and then lets you work your magic in your own app.
Keep in mind you aren't going to call your directory (and filename) blog as well, you'll match the name of your Rails app for simplicity. Otherwise of course you can call it whatever you like.
Now, use javascript_pack_tag
to reference your app.
In development
I'm just popping this in here because it seems to have confused some of the people I've spoken to on the issue. When in development run bin/webpack-dev-server
- this will watch changes to your app and rebuild when required, pushing changes to the browser. When you're ready to compile you can call bin/webpack
or rails assets:precompile
but you're likely to leave that up to your server.
Turbolinks and Rails UJS
The next thing to tackle (if you're using either of these features) is to get Turbolinks and Rails UJS back up and running. In the asset pipeline it's as simple calling //= require
but it's a little different when using them as modules. First, install both the NPM packages.
yarn add rails-ujs turbolinks
Then we need to import and start them up, in our app/javascript/blog/index.js
file (or whatever the name of your app is).
As you can see they use the same signature to kick them off after you've imported them. Pretty easy.
Environment Variables
You can access your environment variables from your scripts as they are compiled. I used to append .erb
to the filename and then do something like <%= ENV['X_ENV_VAR'] %>
but there is a better way. Environment variables are exposed to process.env
.
Stylesheets
You can simply import the CSS or Sass stylesheets you want to use. I've been popping my Sass entry point in app/javascript/blog/styles/app.scss
and then keeping everything I need in the styles
directory. Then it's easy to import in your app's JavaScript.
While on this topic, it's worth learning about the ~
character when using imports as it will start a lookup from the node_modules
directory. So if you wanted to import something like Bootstrap it's as easy as the following.
Boom - Bootstrap 4 ready to go!
Images
Images work in a similar sort of way to stylesheets - you will need to import them in your JavaScript before you can use them. I place my images in app/javascript/blog/images
and then have a app/javascript/blog/images/index.js
file that goes and imports each of the images in the directory. For example, it would look something like this.
Keep in mind this won't actually include the images in your stylesheets, it'll just pull them in through Webpacker so they're available to your app. You can then use the asset_pack_path
helper in your views to reference the files. So, if you wanted to use one of those images you could do something like...
In addition you can reference your images in your CSS/Sass and Webpacker will automatically hook them up for you. Generally the root path will be the entry point to your CSS so you shouldn't need to use absolute paths or anything funky.
Conclusion
This is pretty much a work in progress, it's more of a snapshot of how I'm using Webpacker now. I tried to find other posts about how people are using Webpacker but there's not much out there at the moment. I'm really interested to see what the standard use of Webpacker will look like, or if we'll even come to some sort of standard at all.