Rails 7 + bootstrap + getting first bootstrap example working is a little confusing

I’ve been reading about importMaps and rails 7 and decided to create first app and start a simple bootstrap example with rails 7. I could not

Here is the process.

$ rails new project
$ cd project
# create home controller and home view
$ bin/importmap pin bootstrap
# Select first bootstrap from https://getbootstrap.com/docs/5.1/components/toasts/#live-example
# The goal is to get this example working

Then I add to the application.js

$ subl app/javascript/application.js
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "@hotwired/turbo-rails"
// Here I add Toast just for the example
import { Toast } from 'bootstrap'
import "controllers"

Then I go to localhost:3000 and I see

<html>
  <head>
<style type="text/css">.turbo-progress-bar {
  position: fixed;
  display: block;
  top: 0;
  left: 0;
  height: 3px;
  background: #0076ff;
  z-index: 9999;
  transition:
    width 300ms ease-out,
    opacity 150ms 150ms ease-in;
  transform: translate3d(0, 0, 0);
}
</style>
    <title>Rails7project2</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="csrf-param" content="authenticity_token">
<meta name="csrf-token" content="M7WXSkSRpuT9Nq6kJNGdzyxPxLQV__czRLEW9PdeCbWmkLvpoonAYNZ1pT6BKQ1PFAlOwgSJawAEJdotWVybIA">
    

    <link rel="stylesheet" href="/assets/application-e0cf9d8fcb18bf7f909d8d91a5e78499f82ac29523d475bf3a9ab265d5e2b451.css" data-turbo-track="reload">
    <script type="importmap" data-turbo-track="reload">{
  "imports": {
    "application": "/assets/application-ddfe7a679d279db82d236a96ed63ad21b88239bbd83224d2ff86043c3eb67c74.js",
    "@hotwired/turbo-rails": "/assets/turbo.min-305f0d205866ac9fc3667580728220ae0c3b499e5f15df7c4daaeee4d03b5ac1.js",
    "@hotwired/stimulus": "/assets/stimulus.min-900648768bd96f3faeba359cf33c1bd01ca424ca4d2d05f36a5d8345112ae93c.js",
    "@hotwired/stimulus-loading": "/assets/stimulus-loading-685d40a0b68f785d3cdbab1c0f3575320497462e335c4a63b8de40a355d883c0.js",
    "bootstrap": "https://ga.jspm.io/npm:bootstrap@5.1.3/dist/js/bootstrap.esm.js",
    "@popperjs/core": "https://ga.jspm.io/npm:@popperjs/core@2.11.2/lib/index.js",
    "controllers": "/assets/controllers/index-2db729dddcc5b979110e98de4b6720f83f91a123172e87281d5a58410fc43806.js",
    "controllers/application": "/assets/controllers/application-368d98631bccbf2349e0d4f8269afb3fe9625118341966de054759d96ea86c7e.js",
    "controllers/hello_controller": "/assets/controllers/hello_controller-549135e8e7c683a538c3d6d517339ba470fcfb79d62f738a0a089ba41851a554.js"
  }
}</script>
<link rel="modulepreload" href="/assets/application-ddfe7a679d279db82d236a96ed63ad21b88239bbd83224d2ff86043c3eb67c74.js">
<link rel="modulepreload" href="/assets/turbo.min-305f0d205866ac9fc3667580728220ae0c3b499e5f15df7c4daaeee4d03b5ac1.js">
<link rel="modulepreload" href="/assets/stimulus.min-900648768bd96f3faeba359cf33c1bd01ca424ca4d2d05f36a5d8345112ae93c.js">
<link rel="modulepreload" href="/assets/stimulus-loading-685d40a0b68f785d3cdbab1c0f3575320497462e335c4a63b8de40a355d883c0.js">
<script src="/assets/es-module-shims.min-6982885c6ce151b17d1d2841985042ce58e1b94af5dc14ab8268b3d02e7de3d6.js" async="async" data-turbo-track="reload"></script>
<script type="module">import "application"</script>
  </head>

  <body>
    <h1>Page</h1>

<button type="button" class="btn btn-primary" id="liveToastBtn">Show live toast</button>

<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
  <div id="liveToast" class="toast" 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>

<script>
  var toastTrigger = document.getElementById('liveToastBtn')
  var toastLiveExample = document.getElementById('liveToast')
  if (toastTrigger) {
    toastTrigger.addEventListener('click', function () {
      var toast = new Toast(toastLiveExample)

      toast.show()
    })
  }
</script>
  

</body></html>

An error occurs when selecting the button

(index):55 Uncaught ReferenceError: Toast is not defined
    at HTMLButtonElement.<anonymous> ((index):55)

Same is for new bootstrap.Toast

(index):55 Uncaught ReferenceError: bootstrap is not defined
    at HTMLButtonElement.<anonymous> ((index):55)

What is missing? I guess there has to be a stimulus controller here for the click otherwise we can not use bootstrap, right?

Update: I did create a stimulus controller for the click

import { Controller } from "@hotwired/stimulus"
import { Toast } from "bootstrap"

export default class extends Controller {

  show() {
    const toastLiveExample = document.getElementById('liveToast')
    const toast = new Toast(toastLiveExample)
    toast.show()
  }
};

toast still does not show.

Just ran into the same thing last week. Basically the items you import in application.js are not visible in the .erb

To make them available you need to write something like

window.Toast = Toast();

also can using cssbuilding

Yes they are not, Kind of confusing, i knew they are not, but could not find a guide that mentions it. I wanted to help a developer in our team with this and found it difficult to point them to a good documentation. It is all experience

You can, you can. You are right. It’s a little confusing when you step into a “new with rails” developer shoes and look it from their perspective.

By the way, did you solve this issue? Were you able to use Toastr with Rails 7?

I"m having exactly the same issue - I have a basic Rails7 app with Bootstrap working, i.e. I can use bootstrap CSS components, but when I try to access for example a Modal via var myModal = new bootstrap.Modal(document.getElementById('helpModal'), {}); the console just barfs about bootstrap not being known.

If anyone has a working Rails 7 + Bootstrap template shareable that utilises Bootstrap Javascript on Rails .erb pages I’d appreciate a link.

For this specific case, I believe its more elegant (and easier) to just use a stimulus controller and run this code within the controller.

I tried that too - but maybe did it wrong ? How can I get the use of the bootstrap Modal class in the stimulus controller (which otherwise is connected, the console output is shown) ? If I include the import { bootstrap } below I get Failed to register controller: articles (controllers/articles_controller) SyntaxError: import not found: bootstrap - If I outcomment the import it’s the old ReferenceError: bootstrap is not defined

import { Controller } from "@hotwired/stimulus"

import  { bootstrap } from "bootstrap"

export default class extends Controller {
    connect() {
        console.log("Controller Articles!");
        var myModal = new bootstrap.Modal(document.getElementById('helpModal'), {});
        myModal.modal('show');

    }
}

For reference, this is my app/javascript/application.js

// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "@hotwired/turbo-rails"
import "controllers"

import "bootstrap"

and my config/importmap.rb:

# Pin npm packages by running ./bin/importmap

pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
pin "jquery", to: "https://ga.jspm.io/npm:jquery@3.6.0/dist/jquery.js"
pin "bootstrap", to: "https://ga.jspm.io/npm:bootstrap@5.1.3/dist/js/bootstrap.esm.js"
pin "@popperjs/core", to: "https://ga.jspm.io/npm:@popperjs/core@2.11.2/lib/index.js"

For some reason you need to do

import * as bootstrap from “bootstrap”

To get it to work in your controller

I got it working exactly with Toast.

Wrote a full tutorial. Used stimulus. How this helps everybody that finds it confusing. It is with jsbundling and I will try to improve it to use importmap.

https://kmitov.com/posts/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/

Looking forward to the importmaps version.

Thank you very much Cedric - that was the missing piece; by changing the import statement I can now import and access the bootstrap components !

import { Controller } from "@hotwired/stimulus"

import * as bootstrap from "bootstrap"

export default class extends Controller {
    connect() {
        var myModal = new bootstrap.Modal(document.getElementById('helpModal'), {});
        myModal.show();

    }
}
1 Like
1 Like