DEV Community

Cover image for Integrating Bootstrap into Rails 5
Rob Race
Rob Race

Posted on

Integrating Bootstrap into Rails 5

…and how to use a BootStrap Theme

Bootstrap is an HTML/CSS/JS framework to make it quick and easy to build a responsive website. Bootstrap uses a grid layout system that breaks the page into rows and 12 columns inside each row. This now allows you to structure your markup up in a consistent way.


Note: This tutorial is an excerpt from a chapter in my upcoming book Building a SaaS App in Ruby on Rails on Rail 5. The book will guide you from humble beginnings through deploying an app to production. If you find this type of content valuable, the book is on pre-sale right now! I put the note here to not break Dev.to's meta information automagical stuff.


Bootstrap, now being around for many years has developed an ecosystem of themes as well. There are both premium and free themes available. Personally, I like to use Inspinia as a premium theme or AdminLTE as a free them when building a SaaS application.

For the purpose of the post, and due to its open source and free nature, we will use the AdminLTE theme. Feel free to use any other themes. Though class names and markup structure may change specific to the theme you are using.
This post will also assume you have generated a new Rails app with rails new app_name and running a rails server on the default port 3000.

Quick overview of Grid

Let's dig into the grid system in a little more detail before integrating Bootstrap into your application. As a note before, the grid is a 12 column system that is responsive to the viewport or device width. When you create a row in the grid with

you will be able to fill that row with columns. For example, if you wanted a three column layout it would look like...
<div class="row">
  <div class="col-lg-3">
    <h1>One</h1>
  </div>
  <div class="col-lg-3">
    <h1>Two</h1>
  </div>
  <div class="col-lg-3">
    <h1>Three</h1>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

In the example, you may notice the column class name contains the letters lg. This stands for large and means the columns are referencing the large viewport size. There are four sizes, each corresponding to the greatest pixel width. Extra small devices Phones (<768px). Small devices Tablets (≥768px). Medium devices Desktops (≥992px). Large devices Desktops (≥1200px).

You can use multiple column declarations within the class name to instruct the grid what to do depending on the size of the viewport.

<!-- Stack the columns on mobile by making one full-width and the other half-width -->
<div class="row">
  <div class="col-xs-12 col-md-8">One</div>
  <div class="col-xs-6 col-md-4">Two</div>
</div>
<!-- Columns start at 50% wide on mobile and bump up to 33.3% wide on desktop -->
<div class="row">
  <div class="col-xs-6 col-md-4">One</div>
  <div class="col-xs-6 col-md-4">Two</div>
  <div class="col-xs-6 col-md-4">Three</div>
</div>
Enter fullscreen mode Exit fullscreen mode

To go deeper on the grid system and other CSS functionality of Bootstrap you can visit the documentation.

First Controller, Views

If this is a new app, before you begin implementing the layout, we will need to implement a route and controller to bypass the default welcome page. You will be using the controller in the application as you go along.

Let's also pause to mention that Rails comes with built-in command line generators to generate controllers, models, mailers. As well as generating database migration file or even generating a scaffold of all of those combined. In our case, let's focus on the controller generator. The general format for this generator is rails generate controller NAME [action action] [options]. Thus, our controller generate command will look like:

rails generate controller Activity mine feed
  create  app/controllers/activity_controller.rb
   route  get 'activity/feed'
   route  get 'activity/mine'
  invoke  slim
  create    app/views/activity
  create    app/views/activity/mine.html.slim
  create    app/views/activity/feed.html.slim
  invoke  rspec
  create    spec/controllers/activity_controller_spec.rb
  create    spec/views/activity
  create    spec/views/activity/mine.html.slim_spec.rb
  create    spec/views/activity/feed.html.slim_spec.rb
  invoke  helper
  create    app/helpers/activity_helper.rb
  invoke    rspec
  create      spec/helpers/activity_helper_spec.rb
  invoke  assets
  invoke    coffee
  create      app/assets/javascripts/activity.coffee
  invoke    scss
  create      app/assets/stylesheets/activity.scss
Enter fullscreen mode Exit fullscreen mode

Lets make one quick edit to the route file to define a default route. We will add root to: 'activity#mine' to the bottom of the route file, as follows:

Rails.application.routes.draw do
  get 'activity/mine'
  get 'activity/feed'
  root to: 'activity#mine'
end
Enter fullscreen mode Exit fullscreen mode

Application layout

If you are using the AdminLTE Bootstrap theme and following along, you should download them if you have not already. You can download it from this link or from the support page on the theme preview.

Once you have downloaded and unzipped the folder you will want to copy some files into your Rails application. You can run the following commands or copy the files through your OS's file system GUI.

cp ~/Downloads/AdminLTE-2.3.11/dist/css/AdminLTE.css app/assets/stylesheets/
Enter fullscreen mode Exit fullscreen mode

The download location may vary based on your machine. Also, the version number in the path for AdminLTE may change based on the time you download the theme. Use the folder you have downloaded. That command will copy the main AdminLTE CSS styles. The AdminLTE theme also comes with different colored skins. For this post, I will use the light blue, but you can use any of the included colors. The following command will move the skin CSS styles into your application.

mkdir app/assets/stylesheets/skins
cp ~/Downloads/AdminLTE-2.3.11/dist/css/skins/skins-light-blue.css \
app/assets/stylesheets/skins
Enter fullscreen mode Exit fullscreen mode

Now that the required files are present, you will now instruct the Rails application to use the files. You will first change the assets/stylesheets/application.css file to only require itself and a new style.scss file.

/*
 *
 *= require style
 *= require_self
*/
Enter fullscreen mode Exit fullscreen mode

The new style.scss file will then include the bootstrap base CSS files and the AdminLTE files.

@import "bootstrap-sprockets";
@import "bootstrap";
@import "AdminLTE";
@import "skins/skin-blue-light";
Enter fullscreen mode Exit fullscreen mode

If you reload http://localhost:3000 you will now see that the styling of the default page for the activity#mine page has an updated styling. It should now look more like the following image:

Styled

As you can see while there is no full layout, the styles have updated. We are going to need to do a bit more to implement the whole layout. First, let's update the application layout to use a slim template(replacing the .erb file) and some basic Bootstrap styles:

#app/views/layouts/application.slim
doctype html
html
  head
    title Standup App
    = csrf_meta_tags
    = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
    = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
    = stylesheet_link_tag [
      href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" ]
body.fixed.skin-blue-light
    .wrapper
      = render 'layouts/navigation/layout'
      .content-wrapper
        = yield
      = render 'layouts/navigation/footer'
Enter fullscreen mode Exit fullscreen mode
#app/views/layouts/application.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Standup App</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag ‘application', media: ‘all', ‘data-turbolinks-track': ‘reload'%>
<%= javascript_include_tag ‘application', ‘data-turbolinks-track': ‘reload'), false %>
<%= stylesheet_link_tag href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" %>
  </head>
  <body class="fixed skin-blue-light">
<div class="wrapper">
      <%= render 'layouts/navigation/layout'%>
<div class="content-wrapper">
          <%= yield %>
      </div>
      <%= render 'layouts/navigation/footer' %>
</div>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

There are a few things to note here. Firstly, you may notice the lines that begin with = in the head section. This is slim templates' way of included ruby to have output. Which is different than using - in a template. The single hyphen is used to include ruby but has no output, like an if statement. The output in the head is including Rails asset javascript and CSS. We are also including FontAwesome icons through the last stylesheet inclusion helper.
You may also notice some very specific markup in the body. Here, you will be using the AdminLTE specific classes and HTML hierarchy to style your application in the AdminLTE theme.

Lastly, you will see two other helpers. = render helpers, to include other slim files. Then, = yield which is the Rails' template method for including the template from the controller and action called.

The last thing in the section to do before digging into specific sections in the layout is to modify the application.js to include necessary javascript needed for the theme to work. You will be copying another file from the theme's folder and then making sure it is referenced in your project.

To copy the javascript file you can use the following command or use your machine's file GUI:

cp ~/Downloads/AdminLTE-2.3.11/dist/js/app.js app/assets/javascripts/
Enter fullscreen mode Exit fullscreen mode

Additionally, you will need a third party javascript file from the theme folder as well:

cp ~/Downloads/AdminLTE-2.3.11/plugins/slimScroll/jquery.slimscroll.min.js \
vender/assets/javascripts/
Enter fullscreen mode Exit fullscreen mode

You may notice this file gets copied to a different location. It is considered best practice in a Rails app to have third party javascript in the vender folders.

Now, to make sure the newly copied javascript files are included into the project itself you will modify the application.js file:

//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require bootstrap
//= require jquery.slimscroll.min
//= require_tree .
Enter fullscreen mode Exit fullscreen mode

The first three lines were included by default when generating the new Rails app. These lines include the basic included jQuery, tools and Turbolinks. The lines you will add include Bootstrap from the bootstrap-sass gem. And also includes the slimscroll plugin the AdminLTE theme uses. The last line is another Rails default. Meaning the file will include any javascript file in the app/assets/javascripts folder and its children folders. This line is how the copied app.js file and others got included.

Top Navigation

In regards to the navigation within this application, there will be two main sections. The navigation bar across the top, and a navigation sidebar on the left side of the screen.

From the application file, the navigation files were included by rendering a slim template in a navigation folder (= render 'layouts/navigation/layout'). In this file, you are then including the top navigation and side navigation templates:

#app/views/layouts/navigation/_layout.slim
= render "layouts/navigation/header"
= render "layouts/navigation/sidebar"
Enter fullscreen mode Exit fullscreen mode
#app/views/layouts/navigation/_layout.erb
<%= render "layouts/navigation/header" %>
<%= render "layouts/navigation/sidebar" %>
Enter fullscreen mode Exit fullscreen mode

One thing to note here is the use of the underscore in the template file's name. In Rails, partial template files are designated with an underscore at the beginning of the file name. What are partials? Partials are simply a way to break down templates into smaller chunks. This comes into far greater play in situations such as looping over a collection of items. There, you would make the template chunk to display item information a partial.

The first partial included is the header or top navigation bar template. The approach taken in this post with the layout will be to include example markup. Later you will add, remove or change the markup to meet the expectations of the application being built. Here is the header partial:

# app/views/layouts/navigation/_header.slim
header.main-header
  a.logo href="/"
    | Standup App
  nav.navbar.navbar-static-top role="navigation"
    .navbar-custom-menu
      ul.nav.navbar-nav
        li.dropdown.messages-menu
          a.dropdown-toggle data-toggle="dropdown" href="#"
            i.fa.fa-envelope-o
            span.label.label-success 4
          ul.dropdown-menu
            li.header You have 4 messages
            li
              ul.menu
                li
                  a href="#"
                  .pull-left
                    img.img-circle [ alt=("User Image")
                    src="http://placehold.it/160x160" ]
                  h4
                    | Sender Name
                    small
                      i.fa.fa-clock-o
                      | 5 mins
                  p Message Excerpt
                | \...
            li.footer
              a href="#"  See All Messages
        li.dropdown.notifications-menu
          a.dropdown-toggle data-toggle="dropdown" href="#"
            i.fa.fa-bell-o
            span.label.label-warning 10
          ul.dropdown-menu
            li.header You have 10 notifications
            li
              ul.menu
                li
                  a href="#"
                    i.ion.ion-ios-people.info
                    | Notification title
                | \...
            li.footer
              a href="#"  View all
        li.dropdown.tasks-menu
          a.dropdown-toggle data-toggle="dropdown" href="#"
            i.fa.fa-flag-o
            span.label.label-danger 9
          ul.dropdown-menu
            li.header You have 9 tasks
            li
              ul.menu
                li
                  a href="#"
                  h3
                    | Design some buttons
                    small.pull-right 20%
                  .progress.xs
                    .progress-bar.progress-bar-aqua [ aria-valuemax="100"
                    aria-valuemin="0" aria-valuenow="20" role="progressbar"
                    style=("width: 20%") ]
                      span.sr-only 20% Complete
                | \...
            li.footer
              a href="#"  View all tasks
        li.dropdown.user.user-menu
          a.dropdown-toggle data-toggle="dropdown" href="#"
            img.user-image alt=("User Image") src="http://placehold.it/160x160"
            span.hidden-xs Alexander Pierce
          ul.dropdown-menu
            li.user-header
              img.img-circle [ alt=("User Image")
              src="http://placehold.it/160x160" ]
              p
                | Alexander Pierce - Web Developer
                small Member since Nov. 2012
            li.user-body
              .col-xs-4.text-center
                a href="#"  Followers
              .col-xs-4.text-center
                a href="#"  Sales
              .col-xs-4.text-center
                a href="#"  Friends
            li.user-footer
              .pull-left
                a.btn.btn-default.btn-flat href="#"  Profile
              .pull-right
                a.btn.btn-default.btn-flat href="#"  Sign out
Enter fullscreen mode Exit fullscreen mode
# app/views/layouts/navigation/_header.erb
<header class="main-header">
  <a class="logo" href="/">
Standup App
</a>
  <nav class="navbar navbar-static-top" role="navigation">
    <div class="navbar-custom-menu">
      <ul class="nav navbar-nav">
        <li class="dropdown messages-menu">
          <a class="dropdown-toggle" data-toggle="dropdown" href="#">
            <i class="fa fa-envelope-o">
            </i><span class="label label-success">4</span>
          </a>
          <ul class="dropdown-menu">
            <li class="header">
              You have 4 messages
            </li>
            <li>
              <ul class="menu">
                <li>
                  <a href="#"> </a>
                  <div class="pull-left">
                    <img class="img-circle" alt="User Image" src="http://placehold.it/160x160"> </img>
                  </div>
                  <h4>
                    Sender Name
                    <small>
                    <i class="fa fa-clock-o">
                    </i>5 mins
                    </small>
                  </h4>
                  <p>
                    Message Excerpt
                  </p>
                </li>
              </ul>
            </li>
            <li class="footer">
              <a href="#"> See All Messages</a>
            </li>
          </ul>
        </li>
        <li class="dropdown notifications-menu">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
            <i class="fa fa-bell-o">
            </i><span class="label label-warning">10</span>
          </a>
          <ul class="dropdown-menu">
            <li class="header">
              You have 10 notifications
            </li>
            <li>
              <ul class="menu">
                <li>
                  <a href="#">
                <i class="ion ion-ios-people info">
                </i>Notification title
                </a>
                </li>
              </ul>
            </li>
            <li class="footer">
              <a href="#"> View all</a>
            </li>
          </ul>
        </li>
        <li class="dropdown tasks-menu">
          <a class="dropdown-toggle" data-toggle="dropdown" href="#">
            <i class="fa fa-flag-o">
            </i><span class="label label-danger">9</span>
            </a>
          <ul class="dropdown-menu">
            <li class="header">
              You have 9 tasks
            </li>
            <li>
              <ul class="menu">
                <li>
                  <a href="#">
                  </a> 
                  <h3>
                    Design some buttons
                  <small class="pull-right">20%</small>
                  </h3>
                  <div class="progress xs">
                    <div aria-valuemax="100" aria-valuemin="0" aria-valuenow="20" class="progress-bar progress-bar-aqua" role="progressbar" style="width: 20%">
<span class="sr-only">20% Complete</span>
</div>
                  </div>
                </li>
              </ul>
            </li>
            <li class="footer">
              <a href="#"> View all tasks</a>
            </li>
          </ul>
        </li>
        <li class="dropdown user user-menu">
          <a class="dropdown-toggle" data-toggle="dropdown" href="#">
            <img alt="User Image" class="user-image" src="http://placehold.it/160x160" />
          <span class="hidden-xs">Alexander Pierce</span>
          </a>
          <ul class="dropdown-menu">
            <li class="user-header">
              <img class="img-circle" alt="User Image" src="http://placehold.it/160x160"> </img>
              <p>
                Alexander Pierce - Web Developer
                <small>Member since Nov. 2012</small>
              </p>
            </li>
            <li class="user-body">
              <div class="col-xs-4 text-center">
                <a href="#"> Followers</a>
              </div>
              <div class="col-xs-4 text-center">
                <a href="#"> Sales</a>
              </div>
              <div class="col-xs-4 text-center">
                <a href="#"> Friends</a>
              </div>
            </li>
            <li class="user-footer">
              <div class="pull-left">
                <a class="btn btn-default btn-flat" href="#"> Profile</a>
              </div>
              <div class="pull-right">
                <a class="btn btn-default btn-flat" href="#"> Sign out</a>
              </div>
            </li>
          </ul>
        </li>
      </ul>
    </div>
  </nav>
</header>
Enter fullscreen mode Exit fullscreen mode

As you can see in the header partial. The base Bootstrap navigation markup and styles are applied. Then, AdminLTE applies styles over the top of that to give your application a dashboard feel. Within the header navigation bar is the application's title, dropdowns for messages/notification, and a dropdown to access other parts of the application.

Sidebar Navigation

The second template being included is the sidebar or side navigation bar template. This partial will take the same example markup approach.

# app/views/layouts/navigation/_sidebar.slim
.main-sidebar
  .sidebar
    .user-panel
      .pull-left.image
        img.img-circle alt=("User Image") src="http://placehold.it/160x16" /
      .pull-left.info
        p User Name
        a href="#"
          i.fa.fa-circle.text-success
          | Online
    form.sidebar-form action="#" method="get"
      .input-group
        input.form-control name="q" placeholder="Search..." type="text" /
        span.input-group-btn
          button#search-btn.btn.btn-flat name="search" type="submit"
            i.fa.fa-search
    ul.sidebar-menu
      li.header HEADER
      li.active
        a href="#"
          span Link
li
        a href="#"
          span Another Link
      li.treeview
        a href="#"
          span Multilevel
          i.fa.fa-angle-left.pull-right
        ul.treeview-menu
          li
            a href="#"  Link in level 2
          li
            a href="#"  Link in level 2
Enter fullscreen mode Exit fullscreen mode
# app/views/layouts/navigation/_sidebar.erb
<div class="main-sidebar">
  <div class="sidebar">
    <div class="user-panel">
      <div class="pull-left image">
        <img alt="User Image" class="user-image" src="http://placehold.it/160x160" />
      </div>
      <div class="pull-left info">
        <p>
          User Name
        </p>
        <a href="#">
          <i class="fa fa-circle text-success">
          </i>Online
          </a>
      </div>
    </div>
    <form action="#" class="sidebar-form" method="get">
      <div class="input-group">
        <input class="form-control" name="q" placeholder="Search..." type="text" />
        <span class="input-group-btn">
        <button class="btn btn-flat" id="search-btn" name="search" type="submit">
        <i class="fa fa-search">
        </i></button></span>
      </div>
    </form>
    <ul class="sidebar-menu">
      <li class="header">
        HEADER
      </li>
      <li class="active">
        <a href="#">
      <span>Link</span>
      </a>
      </li>
      <li>
        <a href="#">
        <span>Another Link</span>
        </a>
      </li>
      <li class="treeview">
        <a href="#">
          <span>Multilevel</span>
          <i class="fa fa-angle-left pull-right">
          </i></a>
        <ul class="treeview-menu">
          <li>
            <a href="#"> Link in level 2</a>
          </li>
          <li>
            <a href="#"> Link in level 2</a>
          </li>
        </ul>
      </li>
    </ul>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

As you see, the sidebar partial is a bit smaller and simpler. It contains a little bit of user information, a search input, and links to other parts of the application that you will change later.

Footer

The last major piece of the layout will be the footer. It will be simple and it will be short. Again, it will be another partial. Again the partial will be located in the navigation folder. Here is the slim markup:

# app/views/layouts/navigation/_footer.slim
footer.main-footer
  .pull-right.hidden-xs
    | Anything you want
  strong
    | Copyright © 2016
    a href="#"  Company
  | All rights reserved.

Enter fullscreen mode Exit fullscreen mode
# app/views/layouts/navigation/_footer.erb
<footer class="main-footer">
  <div class="pull-right hidden-xs">
    Anything you want
  </div>
  <strong>
    Copyright © 2016
    <a href="#"> Company</a>
  </strong>
  All rights reserved.
</footer>
Enter fullscreen mode Exit fullscreen mode

By going to http://localhost:3000 you should now see a page that looks like this, or similar if you have deviated on theme or layout style:

Layout

That's it. That is all of the files to copy, files to create and markup to add to give your application a layout that your individual controller action templates will render into. That wasn't so bad, was it?

Top comments (1)

Collapse
 
menjilx profile image
Menj

Please ADD PayPal recurring & subscription payment.