Rails 7 with bootstrap 5 by cssbundling-rails with dart-sass and jsbundling-rails with esbuild. No webpack and webpacker and a salt of Stimulus
Today I put on my “new to Rails hat” and I decided to start a new Rails 7 project using the latest two new sharp knives that we have with Rails 7 to get a job done.
The Job To Be Done is: Demonstrate Bootstrap 5 examples of a Toast in a Rails 7 project.
This is a simple example of a popular Bootstrap 5 component that has both css and js involved. My goal is to build a website that will show a button and when the button is clicked a Toast will be visualised as it is in https://getbootstrap.com/docs/5.0/components/toasts/#live
Provide sharp knives and the menu is omakase
These are two points of Rails doctrine. The menu is omakase, which means that we have a good default for the tools that we are using. We are also given sharp knives to use – cssbundling-rails, jsbundling-rails, Stimulus, dart-sass, esbuild.
The process
I would like to bundle bootstrap 5 in the application. One of the options is to take bootstrap from npm and I would like to demonstrate how we bundle bootstrap from npm. Note that this might not be the right choice for you.
NPM version
Make sure you have npm 7.1+. I was trying for more than an hour to identify an error because my npm version was 6.X
$ npm --version
Rails 7 project
# We pass in the option --css bootstrap that will configure most things for us
$ rails _7.0.1_ new bProject --css bootstrap
# enter the project folder
$ cd bProject
Everything is installed. You have all the dependencies to bootstrap, there is a node_modules folder containing node modules like bootstrap.
Bundling the CSS
cssbundling-rails gem is installed by default in the gem file.
There is an app/assets/stylesheet/application.bootstrap.scss that imports the bootstrap css.
It’s content is:
@import 'bootstrap/scss/bootstrap';
We would be using sass (again from npm) to build this .scss file. There is a script in the package.json
{
"name": "app",
"private": "true",
"dependencies": {
"@hotwired/stimulus": "^3.0.1",
"@hotwired/turbo-rails": "^7.1.0",
"@popperjs/core": "^2.11.2",
"bootstrap": "^5.1.3",
"esbuild": "^0.14.11",
"sass": "^1.48.0"
},
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds",
"build:css": "sass ./app/assets/stylesheets/application.bootstrap.scss ./app/assets/builds/application.css --no-source-map --load-path=node_modules"
}
}
The script build:css will take the app/assets/stylesheets/application.bootstrap.scss and will produce an app/assets/builds/application.css
The file application.css is the one that our application will be using. It is referred in application.html.erb as
<%= stylesheet_link_tag “application”, “data-turbo-track”: “reload” %>
<!DOCTYPE html>
<html>
<head>
<title>BProject</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
</head>
<body>
<%= yield %>
</body>
</html>
The live reload of CSS
In order to change the css and live reload it we must start sass with –watch option as
$ sass ./app/assets/stylesheets/application.bootstrap.scss ./app/assets/builds/application.css --no-source-map --load-path=node_modules --watch
but don’t do this as there is a helper file that we should execute at the end of the article that is – calling ./bin/dev
Bundling the JavaScript
jsbundling-rails is installed in the Gemfile.
There is an app/javascript/application.js that imports bootstrap
// Entry point for the build script in your package.json
import "@hotwired/turbo-rails"
import "./controllers"
import * as bootstrap from "bootstrap"
The application.js is bundled with esbuild as a tool and the command is in the package json
# part of package.json
"build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds",
The result is produced in app/assets/builds
The result is referred by the Rails Assets Pipeline and included in the html by calling javascript_inlcude_tag ‘application’ as in
<!DOCTYPE html>
<html>
<head>
<title>BProject</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
</head>
<body>
<%= yield %>
</body>
</html>
Create a new controller
Initial controller that will be the home screen containing the button.
# app/controllers/home_controller.rb
$ echo '
class HomeController < ApplicationController
def index
end
end
' > app/controllers/home_controller.rb
Create a views folder
# views folder
$ mkdir app/views/home
Create the view
It contains a button and a toast.
$ echo '
<!-- app/views/home/index.html.erb -->
<h1>Title</h1>
<div data-controller="toast">
<button type="button" class="btn btn-primary" data-action="click->toast#show">Show live toast</button>
</div>
<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
<div id="liveToast" class="toast hide" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">Bootstrap</strong>
<small>11 mins ago</small>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
Hello, world! This is a toast message.
</div>
</div>
</div>
' > app/views/home/index.html.erb
Note the code:
<div data-controller="toast">
<button type="button" class="btn btn-primary" data-action="click->toast#show">Show live toast</button>
</div>
Here we have a ‘data-controller’ attribute in the div element and a ‘data-action’ attribute in the button element.
This is how we will connect Stimulus JS as a javascript framework to handle the logic for clicking on this button.
Configure the routes
When we open “/” we want Rails to call the home#index method that will return the index.html.erb page.
$ echo '
# config/routes.rb
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Defines the root path route ("/")
root "home#index"
end' > config/routes.rb
Create a new Stimulus controller
We will use Stimulus as a small prefered framework in Rails for doing JavaScript. When the button in the above HTML fragment is selected the method show() in the controller will be called.
# Use the helper method to generate a stimulus controller
$ bin/rails generate stimulus toastController
# create the content of the controller
$ echo '
// app/javascript/controllers/toast_controller.js
import { Controller } from "@hotwired/stimulus"
import { Toast } from "bootstrap"
// Connects to data-controller="toast"
export default class extends Controller {
connect() {
}
show() {
const toastElement = document.getElementById("liveToast")
const toast = new Toast(toastElement)
toast.show();
}
}
' > app/javascript/controllers/toast_controller.js
Start the dev servers for rails, jsbundling-rails and cssbundling-rails
$ ./bin/dev
Visit localhost:3000
There is a button that could be selected and a toast will appear.
Reply
You must be logged in to post a comment.