How to use Bootstrap with Webpack & Rails Discussion
Thanks for posting this and the previous video on using tailwindcss. It's helpful to see the main themes reinforced in each of the videos. One thing that I'm struggling with is what seems like a violation of POLS and the Rails-way in general where we are now putting SCSS files inside a javascripts folder. I understand that webpacker can just figure this out for us, but if I'm debugging a styling issue in an app I'm unfamiliar with I'm not sure I'd start by looking in a javascripts folder. Is there not a way to keep the separation of concerns explicit and store the stylesheets outside of the javascripts folder?
The recommended place for stylesheets in Rails 6 is still the asset pipeline. Tailwind is a css framework generated by JS so it needs to be more tied to things than Bootstrap.
This is all just side effects of whats going on in modern frontend. Rails is just trying to make that easier, unfortunately it is pretty messy I think, but its also doing more complex things now.
@Chris Bloom
You can set up webpacker to look in app/assets
too. I tend to keep using app/assets/javascripts
and app/assets/stylesheets
, but app/javascripts
for vue/stimulus/… components.
See for example:
https://gist.github.com/risen/076916a3cea81c83cb0752760772d8bf
Hi Chris,
Another concern is the location of custom javascript. I am new to Rails so please excuse my ignorance.
In Rails 5 we would add custom js to their own .coffee files. For Blog model I would write custom js in blog.coffee file and so on.
In Rails 6 all javascript gets added to a single file i.e application.js
How can I keep them seggregated?
I've created a demo app with Rails 6 + Webpacker + Bootstrap (with CoreUI), no Sprockets https://github.com/jasl/cybros_core
What are the advantages of running Rails without Sprockets, or rather, what is YOUR rationale for not including Sprockets.. Very interested to hear why.
Hey Chris ,
I'm getting this error:
Webpacker can't find application in /Users/robthomas/Dropbox/rails/sample/public/packs/manifest.json. Possible causes:
- You want to set webpacker.yml value of compile to true for your environment
unless you are using the
webpack -w
or the webpack-dev-server. - webpack has not yet re-run to reflect updates.
- You have misconfigured Webpacker's config/webpacker.yml file.
- Your webpack configuration is not creating a manifest. Your manifest contains: { }
Extracted source (around line #10):
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
The error is on the
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
I did and I got this:
yarn install v1.16.0
[1/4] 🔍 Resolving packages...
success Already up-to-date.
✨ Done in 0.45s.
Hi there,
What is stylesheet_pack_tag
really for in Rails 6? I mean if you import your styles in packs/application.js
as done in this episode, webpack doesn't generate any css file. Moreover if you inspect your HTML there is no reference to css file neither.
I have done teh same in a sample app, without including stylesheet_pack_tag
and the styles are anyway applied to the view.
This is making me very confused,
The stylesheet pack tag is used for serving stylesheets in production, or in development if you have it configured that way.
@chris any comment on using stylesheet_pack_tag
with Hot Module Reloading (hmr)? The README states:
If you have hmr turned to true, then the stylesheetpacktag generates no output, as you will want to configure your styles to be inlined in your JavaScript for hot reloading. During production and testing, the stylesheetpacktag will create the appropriate HTML tags.
From Sigfrid's comment above, I'm guessing we should avoid stylesheet_pack_tag
and instead import
the css in packs/applications.js
.
This article suggests a similar approach:
In order to do so [use hmr], we need to activate it in
webpacker.yml
and not import the stylesheets viastylesheetpacktag
since they will be included frompacks/application.js
However, it seems to me that the suggested solution still uses stylesheet_pack_tag
:
# packs/application.js
import './stylesheets'
and
# app/views/layouts/application.html.erb
<%= stylesheet_pack_tag 'stylesheets', 'data-turbolinks-track': 'reload' unless Webpacker.dev_server.hot_module_replacing? %>
Hi there,
thanks for all of the webpack/rails 6 tutorials. They've been really helpful in upgrading a very old rails app to the latest and greatest.
I'm having one small error however. I have a couple of Bootstrap modal dialogs that get populated via an AJAX request. These POST data to the server, which responds with a JS file with
$('#myModal').modal('hide');
WIth the old asset_pipeline method this worked, but I'm now getting this error:
Uncaught TypeError: $(...).modal is not a function
if I include a jQuery command in the file it's available, but any bootstrap function isn't. Any idea on how to expose all of the Bootstrap functions to the entire application?
The $(…).modal is not a function is usually caused because scripts are loaded in the wrong order . The browser will execute the scripts in the order it finds them. If in a script you attempt to access an element that hasn't been reached yet then you will get an error. Make sure that you don't have a script above one that it requires.
Plugin dependencies
Some plugins and CSS components depend on other plugins. If you include plugins individually, make sure to check for these dependencies in the docs. Also note that all plugins depend on jQuery (this means jQuery must be included before the plugin files).
Multiple jQuery instances
Sometimes this warning may also be shown if jQuery is declared more than once in your code. The second jQuery declaration prevents bootstrap.js from working correctly. The problem is due to having jQuery instances more than one time. Take care if you are using many files with multiples instance of jQuery. Just leave one instance of jQuery and your code will work.
Thanks for the reply @leneborma
I should have been a little clearer in my explanation. The modal dialog, and all other bootstrap JS (eg tooltips, popovers etc), works well when browsing around the site normally, it's just when I return Javascript from the server after processing the form that appeared in the modal dialog that I get that error.
Step by step:
1) Browse to "New Order Page"
2) Fill in form
3) Click on "Add New Item" button, which opens a modal dialog (via data-behaviour tags) that retrieves a form from the server.
4) Submit the "Add New Item" form. This returns some javascript from the server (via AJAX) which puts a new row on the bottom of the order, and attempts to close the modal dialog. It's at this point that the $('#myModal').modal('hide'); causes an error.
So, I'm not sure why it works at the start, and then stops working when processing the Javascript from the server. The jQuery that adds the new row works, but the modal command fails. There's nothing in the Javascript from the server that reloads the jQuery, so I'm not sure if it's Turbolinks being weird, or if it's something altogether.
Thanks.
Find the solution in the @Jun Jiang repo cybros_core.
We need to expose jQuery to the window object :
# packs/application.js
import JQuery from 'jquery';
window.$ = window.JQuery = JQuery;
Chris thanks for the tutorial.
I've noticed though in a comment you mentioned its still best to use css inside assets/stylesheets.
However with this in place you can't use mixins.
If my style sheet is in javascript/stylesheets the mixin has no problems
When i add @import "../node_modules/bootstrap/scss/bootstrap"; to my stylesheet inside assets stylesheets, the mixins work but thats making webpack redundant so im at a loss.
Doesnt this completely make the asset pipeline redundent or am i missing something ??
You should be able to use mixins in the asset pipeline, as long as your filename ends in .scss so it gets processed with scss.
It won't hurt to use Webpacker for everything. In fact, you need to in some cases like when you're using TailwindCSS.
Images don't really need to go through it since they don't need to be preprocessed and if you have simpler CSS, then the asset pipeline is still around for that.
Thanks for the reply.
I can use mixins if i import bootstrap into a .scss stylesheet inside of assets/stylesheets.
If i use the setup in the video then i can use bootstrap javascript but mixins are not available inside .scss assets/stylesheets. They are only available in javascript/stylesheets.
I thought it may be some error on my side but after trying three different fresh rails projects all had the same outcome
h1 {
color: white;
}
@include media-breakpoint-only(xs) {
h1 {
margin-top: 500px;
}
}
Thats the simple scss im using.
I'll stick to using webpack then for stylesheets. If its all loaded using yarn doesnt make sense to add it another way just for styling.
adding the below into asset pipeline stylesheet fixes the mixin problem though.
@import "../node_modules/bootstrap/scss/bootstrap";
I might just remove the asset pipeline all together seeing as though im not going to use it at all and webpack can handle images too.
Chris,
I followed your instructions, and everything works well except when a view is rendered from an action of a controller that does not inherit from ApplicationController
. If the controller inherits directly from ActionController::Base
, the view is not styled by bootstrap.
What's the deal with that? Any insight you can offer would be greatly appreciated!
Does it render a different layout? And if so, have you included the stylesheet tag in that layout?
Chris, thanks for the qick reply!
Your comment about "layout" inspired me to read the docs and now I understand how controllers decide what layout to use. Inserting layout "application"
in the controller inheriting from ActionController::Base
fixed the issue. I now understand too that I could have made a sperate layout with the same name as the controller, or its parent, and put the stylesheet tag in it, and all would work as expected.
Appreciate your help! Keep up the great work!
Hey Chris,
I'm getting the following output when trying to deploy to Heroku:
2019-12-06T07:10:10.191890+00:00 app[web.1]: * Environment: production
2019-12-06T07:10:13.694812+00:00 heroku[web.1]: State changed from starting to crashed
2019-12-06T07:10:13.476484+00:00 app[web.1]: ! Unable to load application: NoMethodError: undefined method `stylesheets' for Rails:Module
2019-12-06T07:10:13.476614+00:00 app[web.1]: bundler: failed to load command: puma (/app/vendor/bundle/ruby/2.6.0/bin/puma)
2019-12-06T07:10:13.476667+00:00 app[web.1]: NoMethodError: undefined method `stylesheets' for Rails:Module
Since I'm a Rails newbie I'm not sure exactly what this means or what to do. The URL for my app on Heroku is here: https://limitless-basin-97153.herokuapp.com/
And my app on Github is here: https://github.com/raybullard/ruby-rails-tutorial
Have you seen these errors before? Any and all help is appreciated!
Thanks,
Ray
doesnt work on rails 6.0.2.rc1 any advice?
Sounds like you probably missed a step somewhere. I just installed Rails 6.0.2.rc1 and went through the screencast. Everything works correctly.
Hi Chris,
I did yarn add bootstrap jquery popper.js
and import bootstrap into the app.
Also, bootstrap's popover and tooltip style works well.
But when I add a toast in page and run $('div.toast').toast('show')
in chrome's console, it says $(...).toast is not a function
Do you know what's wrong?
Sounds like you missed a step. I just updated the repository to include toasts in the example and they work just fine.
Compare your code with the repository and see what you missed.
Hi Chris,
thanks for your reply.
I just recreate a rails project, add bootstrap and config everything follow your video. And when I put a toast html block in page, it did show.
but when I run $('#toast').toast('hide')
in browser's console, it still warning
Uncaught TypeError: $(...).toast is not a function
at <anonymous>:1:13
is there any thing wrong about how I use toast() function.
Uncaught TypeError: $(...).toast is not a function
at :1:13
The $(…).toast is not a function is usually caused because scripts are loaded in the wrong order . The browser will execute the scripts in the order it finds them.
For example, bootstrap depends on jQuery , so jQuery must be referenced first. So, you must called the jquery.min.js and then bootstrap.min.js like the following:
Multiple jQuery instances
Sometimes this warning may also be shown if jQuery is declared more than once in your code. The second jQuery declaration prevents bootstrap.js from working correctly. The problem is due to having jQuery instances more than one time. Take care if you are using many files with multiples instance of jQuery. Just leave one instance of jQuery and your code will work.
Hi Chris,
thanks so much for this tutorial! :-)
I've been able to follow the process and setup a rails app, that's working like a charm in "development".
When I'm trying to change to "production" with:
export RAILS_ENV=production
rake assets:precompile
rails s
I'm always ending up with my content getting served with no CSS/Bootstrap.
I assume, I'm missing a (few) step(s) when moving to production.
Any hint appreciated! Thanks in advance!
I can`t even imagine that to find info you need for disertation could be so difficult. But I found that information, so that is why I will share with you http://bestresearchpaper.com (the service which can edit your dissertations and make it perfect). Hope there are students and that help you.
Hi i followed the tutorial and my Dropdown menus are not working within bootstrap. Any ideas ?
I'm having exactly the same issue. I've tried every tutorial I can find online but can't solve the problem.
I'll bet you got tricked into using Bootstrap 5 instead of Bootstrap 4 like I did. That means your selectors need to change from:
$('[data-toggle="tooltip"]').tooltip()
$('[data-toggle="popover"]').popover()
to
$('[data-bs-toggle="tooltip"]').tooltip()
$('[data-bs-toggle="popover"]').popover()
Actually, I was even dumber than that. I installed Bootstrap 4.6.0 in my project, but I copied text from the docs for Bootstrap 5.
So I didn't really need to change those selectors, I just needed to copy the right text from the Bootstrap docs.
Hi Chris,
This video is great, thank you very much.
I am having an issue, I wonder if you can help.
Sorry if it's an obvious thing, but I am relatively new and can't find the answer to fix it.
```Processing by PagesController#home as HTML
Rendering pages/home.html.erb within layouts/application
Rendered pages/home.html.erb within layouts/application (Duration: 0.0ms | Allocations: 5)
[Webpacker] Compiling...
[Webpacker] Compilation failed:
[webpack-cli] Failed to load '/Users/Lorenzo/code/LorenzoXavier/planned_maintenance/config/webpack/development.js'
[webpack-cli] TypeError: Cannot read property 'plugins' of undefined
at Object. (/Users/Lorenzo/code/LorenzoXavier/planned_maintenance/config/webpack/environment.js:3:13)
at Module._compile (/Users/Lorenzo/code/LorenzoXavier/planned_maintenance/node_modules/v8-compile-cache/v8-compile-cache.js:192:30)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
at Module.load (node:internal/modules/cjs/loader:973:32)
at Function.Module._load (node:internal/modules/cjs/loader:813:14)
at Module.require (node:internal/modules/cjs/loader:997:19)
at require (/Users/Lorenzo/code/LorenzoXavier/planned_maintenance/node_modules/v8-compile-cache/v8-compile-cache.js:159:20)
at Object. (/Users/Lorenzo/code/LorenzoXavier/planned_maintenance/config/webpack/development.js:3:21)
at Module._compile (/Users/Lorenzo/code/LorenzoXavier/planned_maintenance/node_modules/v8-compile-cache/v8-compile-cache.js:192:30)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1137:10)
Completed 500 Internal Server Error in 2130ms (ActiveRecord: 0.0ms | Allocations: 5754)```
Rails 6 & Bootstrap 5 update
Installing packages
Bootstrap and jQuery can stay the same, although Bootstrap 5 doesn't use jQuery anymore so it's actually not needed for this.
javascript yarn add @popperjs/core
instead of
yarn add popper.js
Importing bootstrap into application.js
import * as bootstrap from 'bootstrap'
Selecting all tooltips
document.addEventListener('turbolinks:load', () => {
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
})
Note: as mentioned above the data attribute has changed data-bs-toggle="tooltip
. You can do the same thing for popovers.
Oh Lord!! I wish this comment was pinned to the top. We're all probabaly following along with this tutorial now and using bs5. I did come across this solution after about an hour of Googling.
Then read the comments hoping I could help someone else possibly stuck with tooltip and popover not triggering. Came across this!!!
Should have read the comments first lol.
Nice work @Nick.
Also going to add that I had issues with the tooltips triggering after I left the page and came back so to help with any conflicts with Turbolinks I changed addEventLstener to:
document.addEventListener('turbolinks:load', function(event) {
Works much better now. Just in case anyone else is having that issue.