Using Tailwind CSS AND Bootstrap side by side in Rails.

Published on January 02, 2024
Written by Daniel Schoppmann

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.

Tailwind CSS and Bootstrap button in the same Rails app

Cheers! 🎉

You're now running Tailwind CSS and Bootstrap side by side in your app. Happy coding!

Subscribe to get future articles via the RSS feed .