Nick Charlton

Tailwind CSS with Rails 6 and Webpacker

I don’t do many things frontend these days, but I’ve wanted to try out Tailwind for a while, and I finally had the opportunity. Alas, it was a Rails app which had no frontend at all (apart from administrate), so I need to start from the very beginning. Here’s how I did it:

Webpacker

I needed to add Webpacker, as I’d initially not done this when generating the application. You should be able to skip this if you already have a working setup. First, I added webpacker to the Gemfile and then ran the generator:

bundle exec rails webpacker:install

This command configures much of what’s needed. The Webpacker directory layout ends up looking like this:

app/javascript
├── packs
│   └── application.js
└── src

packs contain the collection of functionality processed with Webpacker to build the package of JavaScript (and CSS!) that serves your application. The default one being application.js.

In app/javascript/packs/application.js, I have the following:

require("@rails/ujs").start()
require("turbolinks").start()

And then, in the application layout, we’ll add the two hooks required to get JavaScript and styles included:

<%= stylesheet_pack_tag "application", media: "all", "data-turbolinks-track": "reload" %>
<%= javascript_pack_tag "application", "data-turbolinks-track": "reload" %>

Which loads both the JavaScript application pack and the separated stylesheet. These replace any existing stylesheet_link_tag/javascript_link_tag usage.

Tailwind CSS

Then, I added tailwindcss to the package.json using Yarn:

yarn add tailwindcss --dev

And generated a Tailwind CSS config file:

npx tailwindcss init app/javascript/src/tailwind.config.js

The configuration file starts empty and looks like this:

module.exports = {
  theme: {},
  variants: {},
  plugins: [],
}

To setup Tailwind, I’m importing the suggested imports in app/javascript/css/application.css:

@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

Webpacker is using postcss-import, so here I’m not using @tailwind to pull in the dependency as suggested in the documentation.

To configure PostCSS, I needed to add Tailwind in the right place (and overwrite the default location it loads the config file from):

module.exports = {
  plugins: [
    require('autoprefixer'),
    require('postcss-import'),
    require('tailwindcss')('./app/javascript/src/tailwind.config.js'),
    require('postcss-flexbugs-fixes'),
    require('postcss-preset-env')({
      autoprefixer: {
        flexbox: 'no-2009',
      },
      stage: 3,
    }),
  ],
};

Finally, I needed to configure Webpacker to expose a standalone CSS file for every environment in the default section of config/webpacker.yml:

# Extract and emit a css file
extract_css: true

(You can also to delete the same line overridden in production.)

Asset management in Rails is gradually moving towards Webpacker, and this is probably a good thing — standardising around the most common tools used in the JavaScript community. But it’s still a bit confusing if this isn’t something do you often.