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.
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:
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.
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:
// 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:
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 file`app/views/layouts/application.html.erb`. Note that we added a new stylesheet_pack_tag:
<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 [email protected]
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:
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/:
<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