Rob Race

@rob__race

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 Build A SaaS App in Rails 6. The book guides you from humble beginnings through deploying an app to production. The book is now in pre sale, and you can grab a free chapter right now!

Also, the beta for my new project Pull Manager is has been released. If you’re losing track of pull requests, have old ones lingering around or would just love a dashboard that aggregates these requests over multiple services (Github, Gitlab, and Bitbucket), check it out.

Also, the beta for my new project Pull Manager is almost ready. If you’re losing track of pull requests, have old ones lingering around or would just love a dashboard that aggregates these requests over multiple services (Github, Gitlab, and Bitbucket), check it out.

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 theme 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 <div class="row"></div> 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>

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>

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

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

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/

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

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
*/

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";

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:

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'
#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>

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/

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/

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 .

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"
#app/views/layouts/navigation/_layout.erb
<%= render "layouts/navigation/header" %>
<%= render "layouts/navigation/sidebar" %>

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
# 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>

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
# 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>

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.
# 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>

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:

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?

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMI family. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

More by Rob Race

Topics of interest

More Related Stories