How to install TailwindCSS 2.0 on Ruby on Rails 6.1.1
In this tutorial, I’ll walk you step-by-step on how to install TailwindCSS 2.0 on Ruby on Rails.
TailwindCSS is “a utility-first CSS framework packed with classes that can be composed to build any design, directly in you markup.” – https://tailwindcss.com/
To follow along make sure you have the necessary tools/dependencies, and they are updated.
For this guide, I am using Ruby 3.0.0; Rails 6.1.1; Node.js 14.15.4 and yarn 1.22.10.
Generate a new rails application
Let’s start by creating a new rails application.
$ rails new rails-tailwind-demo
At the end you will see something like:

As you can see, starting with Rails 6, Webpacker is the default JavaScript compiler. So, all JavaScript code will be handled by Webpacker.
With the installation done, we can generate a root page so we can test the TailwindCSS installation.
$ bundle exec rails g controller pages home
Nothing fancy. We just generated a new controller (PagesController) with a single action “home“. We will edit our config/routes.rb
file so that this will be our entry page.
1 2 3 |
Rails.application.routes.draw do root to: 'pages#home' end |
Now we can start our Rails server and check our page without any styling. We will also start the Webpack dev server, so that when we change a JavaScript file and reload the page, we don’t have to wait longer for the compilation process.
In one terminal window, run:
$ bundle exec rails s -b 0.0.0.0
And in other terminal window or tab, run:
$ bin/webpack-dev-server
If you visit http://localhost:3000 you will see something like the image below. Note that I slightly changed the text.

Installing Tailwind CSS
TailwindCSS developers recommend that Tailwind is installed in most projects as a PostCSS plugin. Webpacker already comes with PostCSS as a dependency. So for now, we will only install tailwindcss and autoprefixer as JavaScript dependencies using yarn.
$ yarn add tailwindcss autoprefixer
Since Rails 6/Webpacker already includes PostCSS as a dependency, there is a PostCSS configuration file under the root of the project (postcss.config.js), where we need to add tailwindcss and autoprefixer as plugins:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
module.exports = { plugins: [ require('postcss-import'), require('postcss-flexbugs-fixes'), require('postcss-preset-env')({ autoprefixer: { flexbox: 'no-2009' }, stage: 3 }), require('tailwindcss'), require('autoprefixer'), ] } |
Tailwind installation can be customized. For that a config file needs to be created. If you are using VS Code, there is a Tailwind extension that uses this configuration file to determine if the project uses TailwindCSS or not.
To create a Tailwind configuration file we need to run:
$ npx tailwindcss init
The previous command will create the Tailwind config file (/tailwind.config.js) at the project’s root folder.
1 2 3 4 5 6 7 8 9 10 11 |
module.exports = { purge: [], darkMode: false, // or 'media' or 'class' theme: { extend: {}, }, variants: { extend: {}, }, plugins: [], } |
Now, we need to add Tailwind to our CSS. For that, let’s start creating a new file (application.scss) under app/javascript/packs/
with the following content:
1 2 3 4 5 |
// app/javascript/packs/application.scss @import 'tailwindcss/base'; @import 'tailwindcss/components'; @import 'tailwindcss/utilities'; |
Also, open app/javascript/packs/application.js and import our new application.scss file like this:
1 2 3 4 5 6 7 8 9 10 |
import Rails from "@rails/ujs" import Turbolinks from "turbolinks" import * as ActiveStorage from "@rails/activestorage" import "channels" import "./application.scss" Rails.start() Turbolinks.start() ActiveStorage.start() |
With this change, we also need to update our main layout fileapp/views/layouts/application.html.erb
. Note that we added a new stylesheet_pack_tag:
1 2 3 4 5 6 7 8 9 10 |
<head> <title>RailsTailwind</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= 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' %> </head> |
With all this in place, we can now start or restart our webpack-dev-server and see if something changed.
PostCSS plugin tailwindcss requires PostCSS 8.
At this point if you try to start webpack-dev-server you will get an ERROR and the compilation will fail.
ERROR in ./app/javascript/packs/application.scss (./node_modules/css-loader/dist/cjs.js??ref–6-1!./node_modules/postcss-loader/src??ref–6-2!./node_modules/sass-loader/dist/cjs.js??ref–6-3!./app/javascript/packs/application.scss)
Module build failed (from ./node_modules/postcss-loader/src/index.js):
Error: PostCSS plugin tailwindcss requires PostCSS 8.
Migration guide for end-users:
https://github.com/postcss/postcss/wiki/PostCSS-8-for-end-users
at Processor.normalize (/Users/psantos/Projects/Learning/rails-tailwind/node_modules/postcss/lib/processor.js:153:15)
at new Processor (/Users/psantos/Projects/Learning/rails-tailwind/node_modules/postcss/lib/processor.js:56:25)
at postcss (/Users/psantos/Projects/Learning/rails-tailwind/node_modules/postcss/lib/postcss.js:55:10)
at /Users/psantos/Projects/Learning/rails-tailwind/node_modules/postcss-loader/src/index.js:140:12
ℹ 「wdm」: Failed to compile.
We are getting this error because TailwindCSS 2.0 depends on PostCSS 8. But rigth now the installed Webpacker 5 depends on postcss-loader 3.0.0, which depends on postcss 7.0.0.
So, to get rid of this error, we need to force our application to use a postcss 8.x.x and postcss-loader 4.2.0. Note that, while I am writing this guide an updated version of postcss-loader is out. It is the version 5.0.0. I tried with this latest version, but it did not work.
To force our app to use PostCSS 8.x.x and postcss-loader 4.2.0, we need to add/install these dependencies explicitly by running the following command:
$ yarn add postcss postcss-loader@4.2.0
Again, note that I am specifying the exact version for postcss-loader. Otherwise, it will install the latest version which is now 5.0.0 and will cause another error.
Now, we can try running the webpack-server-dev again.
Surprisingly when we try running the webpack-server-dev again, we got another error. Thankfully is a different error :), meaning we are progressing.
ERROR in ./app/javascript/packs/application.scss (./node_modules/css-loader/dist/cjs.js??ref–6-1!./node_modules/postcss-loader/dist/cjs.js??ref–6-2!./node_modules/sass-loader/dist/cjs.js??ref–6-3!./app/javascript/packs/application.scss)
Module build failed (from ./node_modules/postcss-loader/dist/cjs.js):
ValidationError: Invalid options object. PostCSS Loader has been initialized using an options object that does not match the API schema.options has an unknown property ‘config’. These properties are valid:
object { postcssOptions?, execute?, sourceMap?, implementation? }
at validate (/Users/psantos/Projects/Learning/modern-css-with-tailwind/rails-tailwind/node_modules/postcss-loader/node_modules/schema-utils/dist/validate.js:104:11)
at Object.loader (/Users/psantos/Projects/Learning/modern-css-with-tailwind/rails-tailwind/node_modules/postcss-loader/dist/index.js:43:29)
ℹ 「wdm」: Failed to compile.
Digging on the Internet, I found this issue: https://github.com/rails/webpacker/issues/2784
So, the temporary solution for this last error is to open config/webpack/environment.js and add the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
const { environment } = require('@rails/webpacker') function hotfixPostcssLoaderConfig (subloader) { const subloaderName = subloader.loader if (subloaderName === 'postcss-loader') { if (subloader.options.postcssOptions) { console.log( '\x1b[31m%s\x1b[0m', 'Remove postcssOptions workaround in config/webpack/environment.js' ) } else { subloader.options.postcssOptions = subloader.options.config; delete subloader.options.config; } } } environment.loaders.keys().forEach(loaderName => { const loader = environment.loaders.get(loaderName); loader.use.forEach(hotfixPostcssLoaderConfig); }); module.exports = environment |
With these changes in place we can now start or restart our rails server and webpack-dev-server:
$ bundle exec rails s -b 0.0.0.0
and
$ bin/webpack-dev-server
Now, webpack-dev-server will be running and compile without error. And if we try to visit http://localhost:3000 we will see something different from what we saw in the beginning:

I will just update our app/views/pages/home.html.erb file, to add more example so we can see for sure that tailwind is working correctly. I will just copy and paste a code from https://tailwindcss.com/:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<div> <figure class="md:flex bg-gray-100 rounded-xl p-8 md:p-0"> <img class="w-32 h-32 md:w-48 md:h-auto md:rounded-none rounded-full mx-auto" src="https://tailwindcss.com/_next/static/media/sarah-dayan.a8ff3f1095a58085a82e3bb6aab12eb2.jpg" alt="" width="384" height="512"> <div class="pt-6 md:p-8 text-center md:text-left space-y-4"> <blockquote> <p class="text-lg font-semibold"> “Tailwind CSS is the only framework that I've seen scale on large teams. It’s easy to customize, adapts to any design, and the build size is tiny.” </p> </blockquote> <figcaption class="font-medium"> <div class="text-blue-600"> Sarah Dayan </div> <div class="text-gray-500"> Staff Engineer, Algolia </div> </figcaption> </div> </figure> </div> |
Now, refreshing a page will give you something like:

That’s it. We have TailwindCSS 2.0 working with Rails 6.1
Complete Source code can be found here: https://github.com/psantos10/rails-tailwindcss