DEV Community

Amree Zaid
Amree Zaid

Posted on • Updated on

Rails 6 with Bootstrap (Webpacker for JS, Asset Pipeline for CSS)

I haven't really worked on Rails for a while so I decided to take a sneak peek on what's going in Ruby on Rails land.

The first thing I want to try is to integrate Rails with Bootstrap. It seems like a lot of tutorials are focusing on how to use webpack for CSS and JS.

But for this article, I'm going to try Bootstrap's integration with Rails with the asset pipeline for the CSS and webpack for the JavaScript. A snippet from webpacker's README:

Webpacker makes it easy to use the JavaScript pre-processor and bundler webpack 4.x.x+ to manage application-like JavaScript in Rails. It coexists with the asset pipeline, as the primary purpose for webpack is app-like JavaScript, not images, CSS, or even JavaScript Sprinkles (that all continues to live in app/assets).

However, it is possible to use Webpacker for CSS, images and fonts assets as well, in which case you may not even need the asset pipeline. This is mostly relevant when exclusively using component-based JavaScript frameworks.

Let's start by creating new rails app first:

rails new random_app
Enter fullscreen mode Exit fullscreen mode

Make sure we can see the welcome page first

rails db:prepare
rails s
Enter fullscreen mode Exit fullscreen mode

Open https://localhost:3000 and verify that you can see the Rails famous welcome page.

Add bootstrap package and its dependencies:

yarn add bootstrap jquery popper.js
Enter fullscreen mode Exit fullscreen mode

From the Bootstrap's documentation

Also note that all plugins depend on jQuery (this means jQuery must be included before the plugin files). Consult our package.json to see which versions of jQuery are supported. Our dropdowns, popovers and tooltips also depend on Popper.js.

Alright, we have all of the required packages installed so the next step would be loading them.

But before we do that, we need to have a page that will confirm all the setup is correct once we've done with all of these.

Create a controller with a view:

rails g controller Home index
Enter fullscreen mode Exit fullscreen mode

Let's put some Bootstrap's related code in there so that we can test the JavaScript and CSS is actually working.

# app/views/home/index.html.erb
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>

<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="top" title="Tooltip on top">
  Tooltip on top
</button>
Enter fullscreen mode Exit fullscreen mode

It doesn't work right now but making it works will be our target.

Next, we'll take a look at app/assets/stylesheets/application.css:

/*
 * ...
 * 
 *= require_tree .
 *= require_self
 */

Enter fullscreen mode Exit fullscreen mode

require_tree means it'll load everything insider app/assets/stylesheets and it sub directories as well. require_self will load anything we defined in application.scss at the bottom of the output file (as it's declared in the last line).

From Ruby on Rails Guides

In this example, require_self is used. This puts the CSS contained within the file (if any) at the precise location of the require_self call.

If you want to use multiple Sass files, you should generally use the Sass @import rule instead of these Sprockets directives. When using Sprockets directives, Sass files exist within their own scope, making variables or mixins only available within the document they were defined in.

Depending on what you (or your team) are comfortable with, but I prefer to explicitly specify my files.

So, I'll change application.css to a SCSS file so that I can use @import directive:

mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss
Enter fullscreen mode Exit fullscreen mode

Change the content to:

@import 'bootstrap';
Enter fullscreen mode Exit fullscreen mode

Create a new file at app/assets/stylesheets/bootstrap.scss and update the file with:

@import 'bootstrap/scss/bootstrap';

Enter fullscreen mode Exit fullscreen mode

Reload our Home#index and you'll see that Bootstrap's styles have been loaded correctly. However, the tooltip is not working yet. That's the JavaScript part.

Optional: You might be wondering why I didn't just do @import in the application.scss itself. Well, the main reason is that I want to customize which part of Bootstrap I want to use. This will help in terms of file size later. But current code will still load everything, so, let's change it to:

// Required
@import 'bootstrap/scss/functions';
@import 'bootstrap/scss/variables';
@import 'bootstrap/scss/mixins';

// Optional
@import 'bootstrap/scss/root';
@import 'bootstrap/scss/reboot';
@import 'bootstrap/scss/type';
@import 'bootstrap/scss/images';
@import 'bootstrap/scss/code';
@import 'bootstrap/scss/grid';
@import 'bootstrap/scss/tables';
@import 'bootstrap/scss/forms';
@import 'bootstrap/scss/buttons';
@import 'bootstrap/scss/transitions';
@import 'bootstrap/scss/dropdown';
@import 'bootstrap/scss/button-group';
@import 'bootstrap/scss/input-group';
@import 'bootstrap/scss/custom-forms';
@import 'bootstrap/scss/nav';
@import 'bootstrap/scss/navbar';
@import 'bootstrap/scss/card';
@import 'bootstrap/scss/breadcrumb';
@import 'bootstrap/scss/pagination';
@import 'bootstrap/scss/badge';
@import 'bootstrap/scss/jumbotron';
@import 'bootstrap/scss/alert';
@import 'bootstrap/scss/progress';
@import 'bootstrap/scss/media';
@import 'bootstrap/scss/list-group';
@import 'bootstrap/scss/close';
@import 'bootstrap/scss/toasts';
@import 'bootstrap/scss/modal';
@import 'bootstrap/scss/tooltip';
@import 'bootstrap/scss/popover';
@import 'bootstrap/scss/carousel';
@import 'bootstrap/scss/spinners';
@import 'bootstrap/scss/utilities';
@import 'bootstrap/scss/print';
Enter fullscreen mode Exit fullscreen mode

This is based on what we have in the original bootstrap.scss. One of the downside with this approach is that you need to check if this file changed whenever you upgrade Bootstrap as it's like using a private method 😁

Our Bootstrap's stylesheet is working as expected so the next step would be getting the JavaScript to work and for this tutorial, we're going to focus on the tooltip part.

Let's import Bootstrap's JS files:

// app/javascript/packs/application.js

import "bootstrap"
Enter fullscreen mode Exit fullscreen mode

We'll initialize tooltip by adding these code at the end of the file:

$(function () {
  $('[data-toggle="tooltip"]').tooltip()
})
Enter fullscreen mode Exit fullscreen mode

In our intialization, we're using $ for jQuery so we need to make it available globally. We can use these to automatically load the modules instead of import or require them:

// config/webpack/environment.js
const { environment } = require('@rails/webpacker')
const webpack = require('webpack')

environment.plugins.append('Provide', new webpack.ProvidePlugin({
  $: 'jquery',
}))

module.exports = environment
Enter fullscreen mode Exit fullscreen mode

Refresh your Home#index and you should be able to see the tooltip when you hover on the button.

Optional: If you want to specify which JS component that you want to load, you can use these steps:

// app/javascript/packs/application.js

require("./bootstrap")

Enter fullscreen mode Exit fullscreen mode

and

// app/javascript/packs/bootstrap/index.js
import "bootstrap/js/src/alert"
import "bootstrap/js/src/button"
import "bootstrap/js/src/carousel"
import "bootstrap/js/src/collapse"
import "bootstrap/js/src/dropdown"
import "bootstrap/js/src/modal"
import "bootstrap/js/src/popover"
import "bootstrap/js/src/scrollspy"
import "bootstrap/js/src/tab"
import "bootstrap/js/src/toast"
import "bootstrap/js/src/tooltip"

// Tooltip/Other components initialization here
Enter fullscreen mode Exit fullscreen mode

And for our tutorial, we just use the last line which is tooltip and comment out everything else. It'll reduce the size of the final JS (not much different though).

💡 Tip: You can inspect your bundle size by running this command and then upload the output to Webpack Visualizer

./bin/webpack --profile --json > webpack-stats.json
Enter fullscreen mode Exit fullscreen mode

💡 Tip: You'll notice that your JS will only compile the new changes when you refresh the page and that might cause some delay. If we want webpack to compile immediately when we made some changes, we can run this command to monitor the changes and compile them:

./bin/webpack-dev-server
Enter fullscreen mode Exit fullscreen mode

💡 Tip: You can override Bootstrap's default value or in other word, you can create your own theme by specifying the variable value before you import Bootstrap's files. So, it can be something similar to this:

$primary: green;
$secondary: pink;

@import "bootstrap";
Enter fullscreen mode Exit fullscreen mode

Checkout this variables list and you can find more info at Bootstrap's Documentation. There're lots more that you can actually do.

Top comments (2)

Collapse
 
rishudc119 profile image
Deepak Chauhan

I have created a layout file as _navigation.html.erb in which I have my navigation bar code . I want to add background color to my nav bar for that I am using $navbar-default-bg:red; in bootstrap.scss file but nothing is happening why?

Collapse
 
amree profile image
Amree Zaid • Edited

I don't think that variable will do anything just by declaring it.

You can use:

<nav class="navbar navbar-dark bg-primary">

<nav class="navbar navbar-light" style="background-color: #e3f2fd;">

For your red, maybe you can try:

<nav class="navbar navbar-dark bg-dangar">

You can always add your own class and then add it the navbar

# scss
.your-custom-class {
  background-color: red;
}

# html
<nav class="navbar navbar-dark your-custom-class">

Based on the given documentation here:

getbootstrap.com/docs/4.0/componen...