Using Tailwind CSS AND Bootstrap side by side in Rails.
Table of Contents
Why using both Tailwind and Bootstrap?
Compared to a few years ago, it's a blast to develop Rails apps with modern frontend tools these days. The full JS and CSS ecosystems are first class citizens in any modern Rails app these days. Let's quickly forget the sad days of webpackers and the like.
As much as we love Rails as a framework for all business logic related coding, we also love the helping hands of CSS frameworks for our frontend work. Even though Rails 8 will even more embrace things like #nobuild, importmaps etc. we usually stick to CSS frameworks and some of their provided JS components just for simplicity. All served within the Rails app with the jsbundling-rails
and cssbundling-rails
gems.
If you're like us, you've probably used both Tailwind CSS and Bootstrap as well. Obviously, both frameworks have their pros and cons, but each is great for certain needs. When it comes to integrating them with Rails, it's usually described as choosing one of them in advance. So while building the app, you can decide whether to use Tailwind or Bootstrap (or any other alternative framework, of course).
However, in more monolithic Rails applications like the ones we prefer to work on, we often choose to use both Tailwind AND Bootstrap. In our case, it usually boils down to using Tailwind as a more bare bones and custom styling toolset, e.g. for all the user-facing parts of an application where you'd build your own styled components anyway. Bootstrap instead is super easy and fast with all the standard components. It's often used to build pure CRUD areas in the application that don't need a special custom look. A typical use case for this is some kind of admin area within the application.
So can we actually use both Tailwind and Bootstrap in the same application with the ease we expect from Rails?
Oh yes, turns out it's pretty easy to do! Even though Bootstrap is built around sass and Tailwind uses its own compiler, as long as we pipe the build output to the right places, it just works! All we have to do is slightly tweak the build process for each of them. Follow along!
Create a new Rails app
Let’s assume we are building something similar to the use case mentioned above: We’ll use Tailwind for our application layout (and thus call the files application.*
) and Bootstrap for our admin layout (calling the files admin.*
accordingly).
HINT: We are using Rails in version 7.1.2 in this example. You’ll need node
and yarn
installed for the whole JS ecosystem to work.
Let’s start building:
rails new tailwind-bootstrap -j esbuild -c tailwind
The -j
option tells Rails to install the jsbundling-rails
gem as well. We defined esbuild
as our JS bundler, but the other options such as bun, rollup.js etc. should work as well. Using jsbundling-rails
also ensures that a package.json
file ist created, foreman
as a local process manager is installed and yarn install
works.
With the -c
option we define TailwindCSS as our CSS framwork. This will also install the necessary cssbundling-rails
gem and install a working tailwind config named tailwind.config.js.
All those steps can also be done in an existing application. Just be sure to install the proper bundling gems and follow the steps along.
Add the Bootstrap JS packages
Let’s also add the bootstrap related JS packages to the app. This is about the same that is done within the CSSbundling installer scripts.
yarn add sass bootstrap @popperjs/core
Adjust the build scripts in package.json
We distinguish the build process for both Tailwind and Bootstrap here. Again, the build scripts are a direct copy of the installer script in the cssbundling-rails gem files. You can name the commands differently but be aware, that the cssbundling-rails gem expects a build:css
command. It automatically attaches it to the rake assets:precompile
task, so your deployments and your system tests should still work out of the box. Also specifically note the load-path=node_modules
option for Bootstrap. This not only tells where to look for Bootstrap, but also allows to easily add additional packages with specific CSS bundled, like tagging packages, date pickers, etc.
{
...
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds --public-path=/assets",
"build:css": "yarn build:tailwind && yarn build:bootstrap",
"build:tailwind": "tailwindcss -i ./app/assets/stylesheets/application.tailwind.css -o ./app/assets/builds/application.css --minify",
"build:bootstrap": "sass ./app/assets/stylesheets/admin.scss ./app/assets/builds/admin.css --no-source-map --load-path=node_modules"
},
...
}
Adjust the Procfile.dev
The cssbundling-rails
and jsbundling-rails
gem install foreman
as a process tool and a Procfile.dev
file. In order to pick up both changes for tailwind and for bootstrap we need to adjust the processes with the build commands we just entered in the package.json
file.
web: env RUBY_DEBUG_OPEN=true bin/rails server
js: yarn build --watch
tailwind: yarn build:tailwind --watch
bootstrap: yarn build:bootstrap --watch
Add the Bootstrap CSS files
Since we already installed Tailwind CSS for our user facing part of the app while running the Rails generator, we can already proceed to Bootstrap for our admin part of the app.
First we need to create the admin.scss
file:
touch app/assets/stylesheets/admin.scss
Let’s load Bootstrap in this file:
@import 'bootstrap/scss/bootstrap';
Let’s create the admin layout file as a initial copy of the application layout file:
cp app/views/layouts/application.html.erb app/views/layouts/admin.html.erb
Now replace the loading for the application styling file with the admin styling:
- <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
+ <%= stylesheet_link_tag "admin", "data-turbo-track": "reload" %>
Add the Bootstrap JS files
To also take advantage of the Bootstrap JS functionality, we can basically do the same for the asset pipeline as we did for the CSS:
Let’s create an admin.js
file:
touch app/javascript/admin.js
Place code to actually load the Bootstrap related JS, e.g.:
// Entry point for the build script in your package.json
import '@hotwired/turbo-rails'
import * as bootstrap from 'bootstrap'
// Example for using a the bootstrap tooltip with turbo
document.addEventListener('turbo:load', () => {
// tooltip component needs to be initialized
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
})
Create controllers and views
We’ll create some quick controller and views to test everything out.
First add some dummy content for testing the Tailwind setup:
rails generate controller main index
Let’s add a test button styled with some Tailwind classes into app/views/main/index.html.erb
:
<button class="p-2 text-white bg-red-700 rounded bordered">I'm styled with Tailwind</button>
After that let’s repeat this for the admin setup with Bootstrap:
rails generate controller admin index
In the app/views/admin/index.html.erb
file replace the generated content with:
<button class="btn btn-primary">I'm styled with Bootstrap</button>
Caution! The AdminController autoloads the right layout because of our file naming naming. Adjust the layout call per controller if you have a different naming scheme.
Test everything in the browser
Start your app processes with
bin/dev
and watch foreman informing you about the processes output. You should see the compilation step for Tailwind and Bootstrap.
Go to both localhost:3000/main/index
and localhost:3000/admin/index
. You should see Tailwind and Bootstrap working peacefully together! Their build output is dumped into app/assts/builds/*
and picked up by the standard Rails asset pipeline.
Cheers! 🎉
You're now running Tailwind CSS and Bootstrap side by side in your app. Happy coding!