In this post, I'll demonstrate how to set up a Rails application with Bootstrap for Webpacker.
If you'd prefer to skip the post and go straight to the demo app, you can find it here: https://github.com/rossta/rails-webpacker-bootstrap-demo
The examples and demo app described in this post use the following dependencies:
# Ruby/Rails Rails 6.0.1 Ruby 2.6.5 Webpacker 4.2.0 # npm @rails/webpacker 4.2.0 bootstrap 4.3.1 jQuery 3.4.1 popper.js 1.16.0
We'll assume we're working from a recently-created Rails 6 app with the default Webpacker installation. The examples may also work with other versions Rails that support Webpacker 4.
When the Webpacker install is run, i.e.
bin/rails webpacker:install, it adds the file
The file initially looks something like the following:
Tip: If you omit the
extract_css: falseset for your environment in
config/webpacker.yml, then the CSS won't load! The JS bundle is necessary in this case.
To add Bootstrap, install via yarn:
$ yarn add bootstrap
At the time of this post, the above is the equivalent to
yarn add [email protected]. Your installation may vary; I would expect the tutorial here will still work for other versions of Bootstrap 4.
To get Bootstrap css working, add a stylesheet
Note: the file extensions are important, i.e., Webpacker configure files ending in '.scss' and '.sass' to be processed by webpack's
To include our new stylesheet in the build output, we must import it from somewhere in our dependency tree. Let's put this import in the entry point, our
application.js files as the top of separate dependency trees. In webpack, think of your application.js pack as the lone root the dependency tree from which all static assets will be imported; the
application.css bundle is simply a by-product of the build. In other words, there is no need for a separate "stylesheet pack" like
Tip: With webpack, it's recommended to have only one entry point (or "pack" in WebpackER terminology) per page for your bundled assets. For our starter app, the entry point is
Adding SASS overrides
bootstrap.scss uses SASS variables for theme-ing, you can override the defaults with new values.
For example, you can change the background and font colors as follows:
You may also surgically import selected parts of bootstrap to limit bundle size:
yarn add jquery popper.js
application.js bundle (fingerprinted as
js/application-c67c235b5c7d8ac4f1fe.js) is already 940kB in our webpack build:
Version: webpack 4.41.2 Time: 1003ms Built at: 11/25/2019 4:08:14 PM Asset Size Chunks Chunk Names css/application-8d90f960.css 175 KiB application [immutable] application css/application-8d90f960.css.map 377 KiB application [dev] application js/application-c67c235b5c7d8ac4f1fe.js 940 KiB application [emitted] [immutable] application js/application-c67c235b5c7d8ac4f1fe.js.map 1.06 MiB application [emitted] [dev] application manifest.json 640 bytes [emitted] ℹ ｢wdm｣: Compiled successfully.
As an exercise, we might decide to defer the import and initialization of the jquery plugins. Let's consider
jquery as a critical dependency; it is needed as part of the "initial" bundle that blocks the page load while it is parsed and evaluated. But
bootstrap can be deferred; since they are plugins that affect the DOM, they're not as critical, i.e., the DOM needs to be loaded first anyways.
One such deferring technique is dynamic import. webpack will recognize when
import is used as a function, e.g.
import('some-lib'), and pull out the module as a separate "chunk" (another file), that will be loaded asynchronously when the function is evaluated.
In our demo app, we can move
bootstrap to a separate file. Critically, this file is NOT in
Back in the application pack, we replace the
bootstrap imports with a dynamic import of
Version: webpack 4.41.2 Time: 41ms Built at: 11/25/2019 4:03:54 PM Asset Size Chunks Chunk Names css/application-8d90f960.css 175 KiB application [immutable] application css/application-8d90f960.css.map 377 KiB application [dev] application js/0-7f46c35cf4589f8534f7.chunk.js 217 KiB 0 [immutable] js/0-7f46c35cf4589f8534f7.chunk.js.map 257 KiB 0 [dev] js/1-6bb4a0148baccc5762c4.chunk.js 926 bytes 1 [immutable] js/1-6bb4a0148baccc5762c4.chunk.js.map 246 bytes 1 [dev] js/application-0b7847cb72725f896091.js 727 KiB application [emitted] [immutable] application js/application-0b7847cb72725f896091.js.map 835 KiB application [emitted] [dev] application manifest.json 640 bytes [emitted] ℹ ｢wdm｣: Compiled successfully.
We've knocked the
application.js bundle, (now fingerprinted as
js/application-0b7847cb72725f896091.js) down to 727kB. Still sizable, but represents a significant reduction from the first pass.
Another optimization step could be to configure the
SplitChunksPlugin to code-split our bundle programmatically, but we'll save that for another post.
import Rails from "@rails/ujs" import 'jquery' import('src/plugins') // loads async import 'css/site' Rails.start()
And our final directory structure is:
You can also check out the demo app for this post at https://github.com/rossta/rails-webpacker-bootstrap-demo.
I hope this post shed some light on using Bootstrap with Webpacker on Rails and wish you Happy Webpacking!