Say you just bought a Bootstrap template (or any HTML template) and want to integrate it to Rails 6, where do you start? đ€
This tutorial aims to guide the integration process, outlining a few main steps, the whole integration process might take a few hours to a few days depending on how big or complex for the template you bought.
Table of contents
I will be using the Front Bootstrap Theme for this tutorial, you can use your own theme, I think the steps should be similar.
I suggest creating a controller and a view, and route the root path to this view before continuing if you are creating a new Rails app. As the default âYay you are on Railsâ page is not using the NPM packages which we are gonna install the next part, hence you wonât see any error if anything goes wrong, which we donât want as we want to be able to resolve potential javascript issues.
Many templates rely on NPM packages for frontend stuff, we can check which NPM packages they are using by navigating to the javascript or vendor folder of the template :
One thing to note is that not all Javascript libraries used by the template are available as NPM package, some of the Javascript library might be proprietary to the template, or just not available on NPM.
You can check if the Javascript library is available as package by searching it on the NPM website : https://www.npmjs.com
List down the names of the libraries that are available on NPM, then install them all using yarn add
in terminal, for my example :
yarn add @yaireo/tagify appear bootstrap popper.js chart.js chartjs-chart-matrix chartjs-plugin-datalabels circles.js clipboard datatables datatables.net-buttons daterangepicker dropzone flag-icon-css flatpickr ion-rangeslider jquery-mask-plugin jquery-migrate jquery-validation jszip leaflet list.js pdfmake pwstrength-bootstrap quill sortablejs table-edits
(Yes, thats a lot) If you are using bootstrap package, make sure to install âpopper.jsâ package as well, as some bootstrap components relies on popper.js .
Now that we have installed these NPM packages, lets import them in the main pack file in app/javascript/packs/application.js
/* app/javascript/packs/application.js */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
require("@rails/ujs").start()
require("turbolinks").start()
// Uncomment to copy all static images under ../images to the output folder and reference
// them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>)
// or the `imagePath` JavaScript helper below.
//
// const images = require.context('../images', true)
// const imagePath = (name) => images(name, true)
import "@yaireo/tagify";
import "appear";
import "bootstrap";
import "chart.js";
import "chartjs-chart-matrix";
import "chartjs-plugin-datalabels";
import "circles.js";
import "clipboard";
import "datatables";
import "datatables.net-buttons";
import "daterangepicker";
import "daterangepicker/daterangepicker.css";
import "dropzone";
import "flag-icon-css/css/flag-icon.min.css"
import "flatpickr";
import "flatpickr/dist/flatpickr.min.css";
import "ion-rangeslider";
import "jquery-mask-plugin";
import "jquery-migrate";
import "jquery-validation";
import "jszip";
import "leaflet";
import "list.js";
import "pdfmake";
import "pwstrength-bootstrap/dist/pwstrength-bootstrap.min";
import "quill";
import "select2";
import "select2/dist/css/select2.min.css";
import "sortablejs";
Alright now we have imported these NPM packages, lets run rails server and webpack dev server to check if everything is working alright :
rails server
then open a new terminal tab/window :
./bin/webpack-dev-server
Head over to http://localhost:3000 , oh no theres an error!
What does the âModule not found: Error Canât resolve âappearâ â error mean? It means that the import âappearâ; line in the application.js has failed, as there is no âappearâ module to be imported.
Usually modern Javascript package will export their functionality as a module, so that we can import them easily. Say for example the @yaireo/tagify library, we can navigate to node_modules/@yaireo/tagify folder, and open the package.json file :
Thereâs a âmainâ key which tell us which is the main file of this library, in this case its â./dist/tagify.min.jsâ, letâs open it.
If we search the keyword âexportâ in the tagify.min.js file, there should be at least one match :
The âmodule.exportsâ in the Javascript code will export the functionality of the library as a module, most modern Javascript library will have this line.
Now letâs look back at the problematic âappearâ package, navigate to node_modules/appear/package.json and open it :
The âmainâ key refered to âappear.jsâ, which is actually located in dist/appear.js. Letâs open up this file and search for the keyword âexportâ :
Turns out thereâs no export keyword in this library! As its functionality is not exported, we canât import it, this is why âimport âappearâ;â line doesnât work.
To solve this, we can use the ârequireâ statement instead of import, and usually the file to be required is mentioned in the âmainâ key in package.json, which in this case, the âappear.jsâ file.
In your app/javascript/packs/application.js file :
import "@yaireo/tagify";
// change the import to require
// import "appear";
require("appear/dist/appear");
import "bootstrap";
import "chart.js";
Note: For javascript file, we can skip the â.jsâ extension, require("appear/dist/appear")
is equivalent to require("appear/dist/appear.js")
. But for CSS, we must add the â.cssâ extension.
Save it, and refresh your page in web browser, it works now!
If you find yourself facing the âModule not foundâ error after installing NPM package and importing them, you can try change the âimportâ into ârequireâ for the problematic package.
There might also be package that doesnât have âmainâ key specified in their package.json file, in this case, you have to search for the libraryâs javascript file path, and import or require the full path like this :
// there's no "main" key specified in package.json, you have to import the full path to the actual javascript file
// import pwstrength-bootstrap
import "pwstrength-bootstrap/dist/pwstrength-bootstrap.min";
// there's no "main" key specified in package.json, and the javascript file doesn't export as module, we have to use require
require("table-edits/build/table-edits.min");
In some case, the imported library is actually just CSS, you have to import the full path to the CSS file :
import "flag-icon-css/css/flag-icon.min.css"
There might also have some Javascript package that comes with their own CSS file, we have to import these as well so that the styling will apply. For example, the package daterangepicker, flatpickr and select2 comes with their own CSS files, and we need to import them too :
/* app/javascript/packs/application.js */
// ...
import "daterangepicker";
import "daterangepicker/daterangepicker.css";
import "flatpickr";
import "flatpickr/dist/flatpickr.min.css";
import "select2";
import "select2/dist/css/select2.min.css";
âHow do I know which library has their own CSS files?â , I heard you, I also asked the same question, I am no Javascript expert, I only realize I need to import these CSS when I saw the dropdown on the HTML page behave weirdly, I donât have easy answer for this.
For daterangepicker , their getting started section has a stylesheet include, hence I figured I need to import CSS in the application.js as well.
If your Javascript components behave or look weird, chances are they come with their own CSS and you need to import them too.
As I have covered in this guide on how to use Bootstrap and Jquery on Rails 6, we need to use ProvidePlugin to perform shimming so modules can use variables in other modules.
Open config/webpack/environment.js , and add Jquery, Bootstrap and Popper to the ProvidePlugin :
const { environment } = require('@rails/webpacker')
const webpack = require("webpack");
// Add an additional plugin of your choosing : ProvidePlugin
environment.plugins.prepend(
"Provide",
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
Popper: ["popper.js", "default"] // for Bootstrap 4
})
);
environment.loaders.get('sass').use.splice(-1, 0, {
loader: 'resolve-url-loader'
})
module.exports = environment
For custom libraries which is not available on NPM, usually I will create a folder named âvendorâ inside app/javascript, which makes the full path app/javascript/vendor.
Copy paste the custom libraries into the app/javascript/vendor folder :
Then import or require these libraries in app/javascript/packs/application.js , use the full path to the actual Javascript file.
Use âimportâ if the library has exported itself as a module (search âexportâ in the libraryâs javascript file, if not found, then use require), or use ârequireâ if the library didnât export itself as module.
Some libraries might have their own CSS files, remember to import those CSS files as well.
Hereâs a short example of importing those vendor Javascript files :
// app/javascript/packs/application.js
// ....
import "../vendor/babel-polyfill/polyfill.min"
import "../vendor/chart.js.extensions/chartjs-extensions"
import "../vendor/hs-add-field/dist/hs-add-field.min"
import "../vendor/hs-count-characters/dist/js/hs-count-characters"
import "../vendor/hs-counter/dist/hs-counter.min"
import "../vendor/hs-file-attach/dist/hs-file-attach.min"
import "../vendor/hs-form-search/dist/hs-form-search.min"
import "../vendor/hs-fullscreen/dist/hs-fullscreen.min.css"
import "../vendor/hs-fullscreen/dist/hs-fullscreen.min"
import "../vendor/hs-go-to/dist/hs-go-to.min"
import "../vendor/hs-loading-state/dist/hs-loading-state.min"
import "../vendor/hs-loading-state/dist/hs-loading-state.min.css"
import "../vendor/hs-mega-menu/dist/hs-mega-menu.min.css"
import "../vendor/hs-mega-menu/dist/hs-mega-menu.min"
// ...
The â../â means go up one level from packs folder, to the âjavascriptâ folder, then â../vendorâ will access vendor folder from the âjavascriptâ folder.
If you are facing âmodule not foundâ error, remember to check if the libraryâs javascript code has âexportâ keyword in it. If thereâs no âexportâ keyword in it, try use ârequireâ for the library.
There might also be theme-specific javscript files, I usually place them under app/javascript/src folder like this :
Then import them as well inside app/javascript/packs/application.js file :
// app/javascript/packs/application.js
// ...
import "../src/theme-custom";
import "../src/theme.min";
import "../src/hs.chartjs-matrix"
We are done with javascript libraries for now, next we will look into copying CSS files and images.
This tutorial will be using Webpack for handling CSS and images, if you prefer using Asset Pipeline, thats fine too, but it will not be covered by this tutorial.
Copy the CSS and images folder into app/javascript folder like this :
Then create a âapplication.scssâ file inside app/javascript/css folder (or whatever your CSS folder is named, could be âstylesheetsâ etc).
And inside the application.scss file, import all the CSS files inside the css folder :
/* app/javascript/css/application.scss */
@import "docs.min";
@import "theme.min";
Next, head to app/javascript/packs/application.js , and import the application.scss file and also the img and svg folder :
/* app/javascript/packs/application.js */
// import the application.scss we created for the CSS
import "../css/application.scss";
// copy all static images under ../img and ../svg to the output folder,
// and you can reference them with <%= image_pack_tag 'media/img/abc.png' %> or <%= image_pack_tag 'media/svg/def.svg' %>
const images = require.context('../img', true)
const imagePath = (name) => images(name, true)
const svgs = require.context('../svg', true)
const svgPath = (name) => svgs(name, true)
// ...
The code above will allow you to reference and load the application.scss using stylesheet_pack_tag , and use the images using image_pack_tag.
Next, head to app/views/layouts/application.html.erb, and include the stylesheet pack tag so the custom CSS files will be loaded for all layout :
<!DOCTYPE html>
<html>
<head>
<title>PaidTemplate</title>
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<!-- This will load the app/javascript/css/application.scss and its imported CSS files -->
<%= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<%= yield %>
</body>
</html>
Next, we will move to the HTML files.
For this tutorial, I will be copying only one HTML file, but the steps should be similar for all HTML files in your theme.
I picked âapps-invoice-generator.htmlâ page from the theme I purchased, I have created a new view named invoice_generator.html.erb and placed it under the âstaticâ controller I have created.
Copy paste everything between the <body>
and </body>
tag of the template HTML to this view.
Run rails s
and ./bin/webpack-dev-server
, and navigate to localhost to view your page!
p/s: If you saw an error saying module not found: Error: Canât resolve âresolve-url-loaderâ, you can solve it by simply installing this package :
yarn add resolve-url-loader
On first glance, the page looks nice!
Just the some images are missing, as the original theme used relative path to the image folder, we have to change them to use the image_pack_tag.
For example, this svg logo image tag :
<img class="navbar-brand-logo" src="./assets/svg/logos/logo.svg" alt="Logo">
We have to change it into this :
<%= image_pack_tag "media/svg/logos/logo.svg", class: 'navbar-brand-logo', alt: 'Logo' %>
Letâs say an image is located in app/javascript/img/160x160/img2.jpg
We can then replace the âapp/javascriptâ with âmediaâ , for the image_pack_tag :
<%= image_pack_tag "media/img/160x160/img2.jpg", class: 'avatar avatar-xs avatar-circle mr-2', alt: 'avatar' %>
After replacing all the image tags, you are almost set!
As I click around, the dropdown menu doesnât work, and the slide menu doesnât work either, maybe thereâs some Javascript issue I missed?
Right click the web page, and select âInspect elementâ (or âInspectâ if you are using Chrome), then navigate to the Console tab , thereâs a âUncaught ReferenceError: $ is not definedâ error!
This is because the HTML contains <script>
tags that uses $(...)
Jquery function directly, which the â$â is not available on the global scope. (You can read more about this here).
To solve this, we can attach the â$â and âjQueryâ variable to the global scope (or window scope as this is the top most scope used in web browsers). In the pack file (app/javascript/packs/application.js), attach them to global :
// app/javascript/packs/application.js
// ...
// export jquery to global
var jQuery = require('jquery')
// include jQuery in global and window scope (so you can access it globally)
// in your web browser, when you type $('.div'), it is actually refering to global.$('.div')
global.$ = global.jQuery = jQuery;
window.$ = window.jQuery = jQuery;
Refresh the page again, and now the âUncaught ReferenceError: $ is not definedâ error is gone!
Aside from $
, you might also encounter other Uncaught ReferenceError when some proprietary librariesâ classes are used in the view code, for example :
The solution is same as above, we just need to attach the âHSUnfoldâ variable (replace this with the variable name you saw) to the global or window scope, then the view code can access it.
// app/javascript/packs/application.js
// ...
var unfold = require('../vendor/hs-unfold/dist/hs-unfold.min.js')
global.HSUnfold = unfold;
window.HSUnfold = unfold;
Repeat this process until all the variables used in the view code is exposed on the global scope.
After all this, you have successfully integrated your theme into the Rails app! đ„ł (Although just one page, all the best for integrating the rest of it đ)