Skip to main content

How to use Bootstrap with Webpack & Rails Discussion

General • Asked by Chris Oliver

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


Hey Chris ,

I'm getting this error:

Webpacker can't find application in /Users/robthomas/Dropbox/rails/sample/public/packs/manifest.json. Possible causes:

  1. 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.
  2. webpack has not yet re-run to reflect updates.
  3. You have misconfigured Webpacker's config/webpacker.yml file.
  4. 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' %>

You can run bin/webpack to run webpack and see what the compilation error was.


I'm getting

error Command "webpack" not found.

Did you yarn install?


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 via stylesheetpacktag since they will be included from packs/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.

Hey, same issue here, did you find anything out?

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;

Why HTML rendering faster instead css/js on this tutorial? Can they explain to me, thank you


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!


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 27,623+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.