mdv.io

💨🌳 Adding TailwindCSS to RedwoodJS

April 9, 2020

I’ve been playing around with RedwoodJS for some side projects lately and have been really enjoying it. Redwood is a full-stack JS framework built by Tom Preston-Werner (Co-Founder of Github) & others. Essentially, Redwood is Rails for modern JS stacks: opinionated, batteries-included, and well thought out.

Another tool I’ve been using heavily is TailwindCSS, especially when developing sites by writing reusable components, as one does with React and Vue. I can’t really say enough good things about Tailwind – I absolutely love it. My productivity has skyrocketed since I started using it – it’s like supercharging your front-end.

So, since I’ve been really digging these two libraries, the next logical step is to marry the two.

Redwood, meet Tailwind. Tailwind, meet Redwood.

TailwindCSS + RedwoodJS

Let’s do this.

1. First lets get a clean Redwood project going:

yarn create redwood-app ./redwood-with-tailwind
cd redwood-with-tailwind

2. And let’s add a basic homepage

yarn redwood generate page home /

Let’s test it out.

yarn rw dev

Now we’ve got a basic Redwood app up and running at http://localhost:8910 Let’s get things going with Tailwind.

3. Add PostCSS and TailwindCSS

Redwood already ships with Webpack by default, so we just need to add PostCSS and Tailwind to get rolling.

yarn workspace web add postcss-loader tailwindcss autoprefixer --dev

(NOTE: We’ve also added Autoprefixer to give wider browser support. Highly recommended.)

Let’s extend webpack to use PostCSS. We’ll do this by placing a new config file in web/config (which doesn’t exist by default):

mkdir web/config
touch web/config/webpack.config.js

Update this file to look like so:

// web/config/webpack.config.js

const configDir = __dirname

module.exports = (config) => {
  config.module.rules[0].oneOf[5] = {
    test: /\.css$/,
    sideEffects: true,
    use: [
      'style-loader',
      { loader: 'css-loader', options: { importLoaders: 1 } },
      {
        loader: 'postcss-loader',
        options: {
          config: {
            path: configDir,
          },
        },
      },
    ],
  }

  return config
}

This will ensure post-css is used to compile CSS. Next we’ll create a postcss config file:

touch web/config/postcss.config.js

And add the following contents:

// web/config/postcss.config.js

const path = require('path')

module.exports = {
  plugins: [
    require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.js')),
    require('autoprefixer')
  ],
}

This ensures Tailwind is used when PostCSS is compiling the CSS. Almost there. Next we’ll initialize TailwindCSS:

yarn workspace web tailwindcss init
mv web/tailwind.config.js web/config/tailwind.config.js

And finally, we need to mport the ase Tailwind styles. Place the following in our web/src/index.css file:

@tailwind base;
@tailwind components;
@tailwind utilities;

That’s it.

Now, let’s make sure it works.

Let’s start the server:

yarn rw dev

🎉 Tada. No styles.

Let’s add something. Open up web/src/index.css and add:

body {
  @apply bg-indigo-500;
}

Save the file and check out your browser. You should be seeing purple.

Well done. 👏

Everything is working right now, but it can be slightly better.

Taking it a step further: PurgeCSS

Tailwind is amazing but there’s very little chance you’re going to use the entire library in your app. Luckily, there’s PurgeCSS to help remove unused css classes and make your payload tiny. Let’s add it.

yarn workspace web add @fullhuman/postcss-purgecss --dev

And all we have to do is update our web/config/postcss.config.js file:

// web/config/postcss.config.js

const path = require('path')

const purgecss = require('@fullhuman/postcss-purgecss')({
  content: [
    path.resolve(__dirname, '..', 'src', '**', '!(*.test).js'),
    path.resolve(__dirname, '..', 'src', '**', '*.html'),
  ],
  // This extractor is used for tailwind classes.
  // Read more here: https://tailwindcss.com/docs/controlling-file-size/
  defaultExtractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [],
})

module.exports = {
  plugins: [
    require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.js')),
    require('autoprefixer'),
    ...(process.env.NODE_ENV === 'production' ? [purgecss] : []),
  ],
}

This will cause PurgeCSS to look at all of our non-test files in the web/src directory, and remove any CSS that’s not used in those files.

⚠️ Note: this config only runs PurgeCSS in production, which is a sane default here. Otherwise you’d have to restart your redwood process on each CSS change. It is a good idea to test the changes in development before shipping, however.

One final change needs to be made. Open up web/src/index.css and modify the Tailwind imports like so:

/* purgecss start ignore */
@tailwind  base;
@tailwind  components;
/* purgecss end ignore */

@tailwind  utilities;

And there you have it, all the power of TailwindCSS inside of RedwoodJS, without any extra bloat.

Happy coding.