Using Tailwind CSS with Create React App

By Dave Ceddia updated

I’ve been hearing a lot about Tailwind CSS lately and I wanted to give it a try, but I wanted to use Tailwind in a Create React App project. In this post I’ll show you how to use Create React App with Tailwind CSS in just a couple quick minutes.

I’ll show two versions: a Basic one that has the most minimal setup, and a Production version that will set things up for an optimized workflow and a smaller production build.

Here’s how to integrate Tailwind with CRA, without ejecting. We will:

  • Install Tailwind, which comes with a build tool
  • Add a build step to generate Tailwind’s CSS
  • Import the generated CSS file into our React app
  • Use Tailwind!

If you want to see me put together the Production config, watch this video! Otherwise, keep on reading (everything is covered in the post below)

Use Tailwind with Create React App

What’s Tailwind CSS?

Tailwind is a low-level CSS framework created by Adam Wathan. Their site describes it as “A utility-first CSS framework for rapidly building custom designs.”

The idea behind Tailwind is that it gives you a bunch of utility classes, like text-lg (for a large-ish font size) or mx-auto (short for margin-left: auto; margin-right: auto;, useful for centering things horizontally).

You apply these utility classes directly to your HTML or JSX elements (via the className prop if you’re using React).

Not everyone loves this idea. But some people absolutely do love Tailwind and talk about how productive it has made them. I think it’s worth a try.

(If the idea of adding such low-level classes to your HTML bothers you, check out In Defense of Utility First CSS by Sarah Dayan and have a look at the screencast tutorials that Adam created. They might change your mind.)

How does Tailwind fit in with React?

A lot of people think React needs special React-specific CSS solutions – things like styled-components or a design framework like Material UI. It actually doesn’t, though.

In fact, React works perfectly with plain CSS files, because React’s only job is to render DOM elements to the page. Those DOM elements can have CSS class names (applied with the className prop in React), and the browser will take care of applying the appropriate styles.

Tailwind is a plain CSS library. It works perfectly with React, and any other UI framework that renders to the DOM. All you need to do is ensure Tailwind’s CSS file is on the page, either via a <link> tag, or an import if you’re using a bundler.

In this article, we’re setting up Create React App, which uses Webpack for bundling. I’ll show you how to import Tailwind.

After that, you can go read the Tailwind docs, mentally replacing class with className everywhere. There is no special dialect of Tailwind for React.

Start With a React project

We’ll be modifying a Create React App (CRA) project, so create a new one or use one you already have. The steps after this will work fine with an existing project.

npx create-react-app demo

Set Up the Build

I’ve got two versions for you. Basic and Production.

Choose Basic if you just want to try out Tailwind with React, don’t plan on deploying this app, and want the most minimal possible setup. It’ll require a dev server restart any time you change the tailwind.css file or its dependencies, but if you’re doing things the Tailwind Way (adding classes to elements instead of writing CSS), you won’t really be changing that file anyway.

Choose Production if you want something you can work with day-to-day and eventually deploy. This sets up a live rebuild that watches for changes, and PurgeCSS to make sure your production builds are as tiny as possible. The tradeoff is more configuration up front, but it’s still just a few packages and lines to copy into place.

Setup a Basic Tailwind + CRA Build

1. Install Tailwind

Tailwind comes with its own CLI tool for doing a build, so all we need is the tailwindcss package.

npm install tailwindcss

2. Add Tailwind to the Build

To avoid ejecting from Create React App’s build system we’re going to insert a step that builds Tailwind before the existing start and build scripts.

Open package.json in the CRA project, add a script called build:tailwind and two more called prestart and prebuild.

(if you haven’t modified your scripts, you should be able to copy/paste this verbatim)

"scripts": {
  "build:tailwind": "tailwindcss build src/tailwind.css -o src/tailwind.output.css",
  "prestart": "npm run build:tailwind",
  "prebuild": "npm run build:tailwind",
  "start": "react-scripts start",
  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject"
}

The build:tailwind script will compile our src/tailwind.css file and save it to src/tailwind.output.css – which our app will then import.

We’re effectively saying “build Tailwind before starting up dev mode” and “build Tailwind before building the React app for production”.

(Note: The script name build:tailwind isn’t special. You could call it build:css or awesomeness or happydays if you wanted.)

NPM supports pre- and post- scripts

Fun fact: Scripts that start with the word “pre” are special to NPM (and Yarn). They automatically run before the named script.

Here, prestart will run before the start script. When you run npm start, NPM will run both automatically.

The prefix “post” will do the same kind of thing, but will run after the named script. (e.g. postbuild would run after build is finished)

A One-Time Build

This setup will only build the CSS once, at startup. It will not watch for changes.

If you change src/tailwind.css you’ll have to restart the development server. If you need automatic rebuilds, check out the Production setup below.

3. Set up the Tailwind source CSS file

(this step is the same as in the Production setup)

You probably noticed that our build step refers to a src/tailwind.css file. Create that now, and paste this in:

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

The Tailwind PostCSS plugin will replace these @tailwind directives with Tailwind’s generated CSS and write it out to src/tailwind.output.css (that name is decided by the build:tailwind script we added to package.json).

4. Import the Generated CSS File

(this step is the same as in the Production setup)

At the top of your index.js file, import the tailwind.output.css file that’s being generated by Tailwind. This one import will make Tailwind’s utility classes available to the entire app, no need to import it again elsewhere.

Now we can try out a few Tailwind classes to make sure it works.

import React from 'react';
import ReactDOM from 'react-dom';
import './tailwind.output.css';

const App = () => (
  <div className="max-w-md mx-auto flex p-6 bg-gray-100 mt-10 rounded-lg shadow-xl">
    <div className="ml-6 pt-1">
      <h1 className="text-2xl text-blue-700 leading-tight">
        Tailwind and Create React App
      </h1>
      <p className="text-base text-gray-700 leading-normal">
        Building apps together
      </p>
    </div>
  </div>
);

ReactDOM.render(<App />, document.querySelector('#root'));

Start up the development server as usual with npm start.

That’s all there is to it!

Where to Make Changes

Don’t change tailwind.output.css since your changes will be wiped out the next time you start up the dev server or run a production build. Instead, put changes in src/tailwind.css and restart the server/re-run the build.

I’d probably avoid committing tailwind.output.css to source control since it’s a generated file. Add src/tailwind.output.css to your .gitignore file if you want to avoid committing it.

Production Tailwind + CRA Build

1. Install Tailwind and Tools

We need Tailwind itself, and a couple tools to build it – namely, chokidar-cli to watch for changes & rebuild, and npm-run-all to run the file watcher and CRA in parallel.

Install ‘em all at once.

npm install tailwindcss npm-run-all chokidar-cli

2. Add a Step to the Build

To avoid ejecting from Create React App’s build system we’re going to modify package.json so that the npm start command will run both the Tailwind build and the CRA server.

The run-p command (run in parallel) that comes with the npm-run-all package is the secret sauce here.

Update the scripts section of package.json to look like this. (unless you’ve customized your scripts, you should be able to copy/paste this verbatim)

"scripts": {
  "build:tailwind": "tailwind build src/tailwind.css -o src/tailwind.output.css",
  "watch:tailwind": "chokidar 'src/**/*.css' 'src/**/*.scss' --ignore src/tailwind.output.css -c 'npm run build:tailwind'",

  "start": "npm-run-all build:tailwind --parallel watch:tailwind start:react",
  "start:react": "react-scripts start",

  "prebuild": "run-s build:tailwind",

  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject"
},

The build:tailwind script is the same as in the Basic version – it runs the Tailwind CLI tool and generates the CSS that we can import into our app.

The watch:tailwind script will re-build the Tailwind output file when any CSS or SCSS files under the src directory are changed. It’s explicitly ignoring the tailwind.output.css file so that we don’t get an infinite loop of rebuilds.

(Note: The script name build:tailwind isn’t special. You could call it build:css or awesomeness or happydays if you wanted.)

We renamed CRA’s existing start script to start:react so that we can add our own start script.

Our start script does a few things:

  • first, a complete (re)build of Tailwind
  • then, in parallel, watch for CSS changes and start the CRA dev server

CRA and Tailwind, co-existing harmoniously.

After you make these changes, start everything up with npm start and try changing src/tailwind.css. Chokidar will detect the change, write out a new src/tailwind.output.css file, then CRA will detect that change and rebuild the React app, and the browser should refresh. Phew.

It’s a bit of a Rube Goldberg machine, but then again, so are most builds :)

Don’t Skip the Build at Startup

You might be tempted to skip that first build of Tailwind before running the parallel steps…

"start": "npm-run-all build:tailwind --parallel watch:tailwind start:react",

When I tried combining the initial build with the “watch” step, CRA would usually start at a blank page and I’d have to manually refresh to get it working.

I think this is a timing problem, where the tailwind.output.css file is erased at the start of its build, at the same time that CRA is loading that file. The fix was to make sure the build is done and finished before CRA starts up.

NPM supports pre- and post- scripts

Fun fact: Scripts that start with the word “pre” are special to NPM (and Yarn). They automatically run before the named script.

Here, prebuild will run before the build script. When you run npm run build, NPM will run both automatically.

The prefix “post” will do the same kind of thing, but will run after the named script. (e.g. postbuild would run after build is finished)

3. Set up the Tailwind source CSS file

(this step is the same as in the Basic setup)

You probably noticed that our build step refers to a src/tailwind.css file. Create that now, and paste this in:

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

The Tailwind build will replace these @tailwind directives with Tailwind’s generated CSS and write it out to src/tailwind.output.css (that name is decided by the build:tailwind script we added to package.json).

4. Import the Generated CSS File

(this step is the same as in the Basic setup)

At the top of your src/index.js file, import the tailwind.output.css file that’s being generated by PostCSS and Tailwind.

Then we can try out a few Tailwind classes to make sure it works.

import React from 'react';
import ReactDOM from 'react-dom';
import './tailwind.output.css';

const App = () => (
  <div className="max-w-md mx-auto flex p-6 bg-gray-100 mt-10 rounded-lg shadow-xl">
    <div className="ml-6 pt-1">
      <h1 className="text-2xl text-blue-700 leading-tight">
        Tailwind and Create React App
      </h1>
      <p className="text-base text-gray-700 leading-normal">
        Building apps together
      </p>
    </div>
  </div>
);

ReactDOM.render(<App />, document.querySelector('#root'));

Start up the development server as usual with npm start.

5. Purge Unused CSS Classes for a Smaller Download

Tailwind generates a HUGE number of CSS classes by default, over 10000 utility classes. You very likely won’t use all of them in your app. It’s like a buffet: take what you want, leave the rest.

PurgeCSS can leave the unused classes out of the final build, as long as you tell it where to look.

Tailwind 1.4.0 added built-in support for PurgeCSS, so we only need to add a little configuration to turn it on, and then our CSS build output will be WAY smaller. Your users will thank you.

Create a tailwind.config.js file in the root of your project (in the same folder as package.json), and paste this in:

module.exports = {
  purge: [
    'src/**/*.js',
    'src/**/*.jsx',
    'src/**/*.ts',
    'src/**/*.tsx',
    'public/**/*.html',
  ],
  theme: {
    extend: {},
  },
  variants: {},
  plugins: [],
}

All of this is default stuff, except the items in the purge array. That’s the list of files that PurgeCSS will scan for CSS class names. If a CSS class is found in one of those files, PurgeCSS will make sure to include it in the build output – otherwise, it’s ripped out.

This list should cover most React apps, whether you’re using Tailwind with TypeScript or regular JS. But if you have other files in your project that have CSS class names in them, make sure to add them to this array.

Where to Make Changes

Don’t change tailwind.output.css – your changes will be overwritten! Instead, put changes in src/tailwind.css and restart the server/re-run the build.

I’d probably avoid committing tailwind.output.css to source control since it’s a generated file. Add src/tailwind.output.css to your .gitignore file to leave it out.

Customize Tailwind

Tailwind has a bunch of good defaults. Nice set of colors. Good array of font sizes. Logical naming of classes. You definitely don’t need to configure anything out of the box. But if you want to customize it, the tailwind.config.js file is the place to do it.

We already created a minimal config, but you can generate a full one for tweaking (or just for reference) by running npx tailwind init tailwind.config.example.js --full. Feel free to replace the one we made, but remember to copy over the purge config if you do.

Learn More About Tailwind

Hopefully this post got you up and running with Tailwind CSS. To learn more, check out their excellent documentation, and especially the screencasts that Adam put together. It was really helpful for me to see how he used Tailwind in practice, and the videos cover a lot of common things you’d want to build like card layouts, badges, navbars, and dropdown menus.

If you want to work on your general CSS chops (which will help you pick up Tailwind faster too), check out this CSS layout tutorial and the challenge I ran a while back.