loading...

Setting up Tailwind With create-react-app

hagnerd profile image Matt Hagner Updated on ・8 min read

What is Tailwind?

Tailwind is a functional CSS framework that is ergonomic to use, but low level enough to make it fully customizable. You can configure it, add plugins, and override defaults. It generates CSS class names for you so that you can use compose them throughout your project.

I've found that Tailwind lends itself particularly well to developing components in React and Vue.


What does it look like?

import React from 'react'

export default function Input(inputProps) {
  return (
    <input 
      className="px-2 py-1 text-gray-700 bg-gray-200 rounded-lg shadow-md border-2 border-gray-800 focused:border-blue-400"
      {...inputProps} 
    />
  )
}

What do all of those classes mean? Most of the classes should be pretty self explanatory. The px-2 and py-1 are horizontal (x), and vertical (y) padding respectively. The 2 and 1 refer to the sizing.

By default Tailwind generates a set of sizes for you that you can customize. Sizing 1 starts at 0.25rem and the sizing goes up by 0.25rem each step.

The class naming follows pretty easy to understand conventions so once you start learning some you will understand how to use most. For instance to set a margin vertical margin of 2 rem you would use the class name my-8. m because you are setting margin, y because you want to set only the vertical axis margin, and 8 because you want 2 rem and the sizing is 4 per rem.

Things that can accept a color value like text, border, or background have their prefix text, border and bg, followed by the color name text-gray, border-gray or bg-gray and then a value from 100-900 that jumps by 100. So text-gray-700 will make the text a fairly dark gray, and bg-gray-200 will give the background a fairly light gray color.

The focused:border-blue-400 class applies a blue 400 color to the border when the focused pseudo class is active for the element.

rounded has a number of suffixes to affect the class like sm, lg, and full with the default being a medium rounded border if there isn't a suffix. There is even the ability to change any corner individually.

shadow is similar to rounded but with the default being small with no suffix, and sizing all the way to 2xl. Additional modifiers that make sense for a box shadow are also available like inner or outline.


Why would you use it?

When you get into the flow it's like writing regular CSS with shorthands except you don't have to do it in a separate file, you don't have to come up with a bunch of class names, and you don't have to possibly update two files every time you change the styles for a single element.

It makes your code easier to delete. We'll touch on this more later, but traditional CSS is append only, which means it is really hard to know when you are okay to delete some styles.

Component based styling, which you can absolutely do with Tailwind, allows you to delete the styles along with the component when you no longer need it.

Tailwind is also totally and completely extendable. Want to add different colors, or change the ones included with Tailwind? You totally can and the API to do so is pretty well documented and easy to follow.

How do we set up create-react-app to use Tailwind?

Let's set up our project by scaffolding a new react app with create-react-app. If you don't have it installed you can use npx.

npx create-react-app setting-up-tailwind && cd setting-up-tailwind

Now we need to install some dev dependencies.

yarn add -D tailwindcss autoprefixer postcss-cli

In the root of the project create a postcss.config.js file and open it up in your favorite editor.

module.exports = {
  plugins: [
    require('tailwindcss'),
    require('autoprefixer'),
  ]
}

If you're interested in finding out more about PostCSS check out the Github

Autoprefixer is recommended to install alongside Tailwind, because autoprefixer automatically tracks caniuse.com to see which CSS properties still need to be prefixed, and out of the box Tailwind does not provide any vendor prefixing.

Now we should initialize Tailwind. This will create a tailwind.config.js file in the root of our project with a default configuration. This step is optional, but I usually do this when setting up a Tailwind project so that I can customize things later without having to come back.

npx tailwind init

If you open it up it looks pretty barren right now. Maybe in a different post I'll go over adding plugins, or customizing Tailwind.

// tailwind.config.js
module.exports = {
  theme: {
    extend: {}
  },
  variants: {},
  plugins: []
}

We also need to create an input CSS file for PostCSS to process with Tailwind. I usually call this tailwind.css and add it to the src folder in my React projects, but you can name it whatever, and place it in any place that makes sense to you.

/* src/tailwind.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

These are Tailwind directives that add the three main parts of core Tailwind. You can make your bundle smaller by omitting one or multiple if you don't need them, but to get the most from Tailwind you will probably end up using at least some classes from each.

When Tailwind (the first plugin in PostCSS) sees these directives it will replace each @tailwind <name> with some CSS.

To make it easy on ourselves in the future case where we might be changing the tailwind.config.js we should add a few scripts to our package.json file. Add the following three scripts to the scripts object.

// package.json
{
  //...
  "scripts": {
    //... place these after the four scripts created by CRA
    "build:styles": "postcss src/tailwind.css -o src/styles.css", 
    "prebuild": "yarn build:styles",
    "prestart": "yarn build:styles"
  }
}

Or if you use npm change yarn to npm run

{
  //...
  "scripts": {
    //... place these after the four scripts created by CRA
    "build:styles": "postcss src/tailwind.css -o src/styles.css",
    "prebuild": "npm run build:styles",
    "prestart": "npm run build:styles"
  }
}

Building our React component

Let's delete some of the unnecessary stuff that create-react-app makes for us.

rm src/App.test.js src/App.css src/index.css src/logo.svg

Open up src/index.js and make the following changes.

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './styles.css' // <- change './index.css' to './styles.css'
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();

Now open up src/App.js, delete the whole thing and start from scratch.

// src/App.js
import React from "react";
import Button from "./components/button";

function App() {
  return (
    <div className="flex flex-col w-3/4 mx-auto my-12 items-center">
      <h1>Super cool page</h1>
      <Button onClick={() => console.log("I was clicked")}>
        I am a button
      </Button>
    </div>
  );
}

export default App;

Let's create a simple button component, this will be a small wrapper around a normal button, but will contain some styles. I'm making this component in a components directory inside of src, but you can put the component wherever you want.

// src/components/button.js
import React from "react";

export default function Button({ children, ...buttonProps }) {
  return (
    <button
      className="px-2 py-1 rounded-lg bg-green-400 text-green-800 text-xl font-light uppercase shadow-md hover:shadow-lg"
      {...buttonProps}
    >
      {children}
    </button>
  );
}

If you run yarn start now you should see that PostCSS is processing our styles for us, and then you should see something like this.

Such beauty. It is almost too much to behold!


Checking our app out in production

So our app is looking great now and we are ready to send it off into the world, but first we need to build for production.

yarn build

Now to check our production build, we can use a tool like serve. Either install it globally, yarn global add serve or you can use npx.

If you installed globally you'll use

serve -s build

or if you want to use npx

npx serve -s build

Sweet! Our page looks pretty rad if I do say so myself. Now let's just open up the developer tools in our browser, click on the network tab, refresh the page and see how slim our sleek new CSS is...

Look at the size of the CSS bundle. 350KB... Yikes! Why is it so big!?

Well Tailwind generates classes. A lot of classes. The stylesheet that it generates is over 3000 lines long. But we are only using a fraction of those classes right now so what can we do?

Slimming Our Build

There is a utility called PurgeCSS which will parse any files that match the given file globs for the usage of the selectors in your CSS. If a selector isn't present in any of the matched files, then it rips those styles out of the CSS, ultimately slimming the build.

There is a PostCSS plugin for PurgeCSS so we can just install our new dependency, and add a little bit more set up to postcss.config.js.

yarn add -D @fullhuman/postcss-purgecss

Open up your postcss.config.js file and make some additions. The following set up is taken directly from the Tailwind docs.

// postcss.config.js
const purgecss = require('@fullhuman/postcss-purgecss')({

  // Specify the paths to all of the template files in your project 
  content: [
    './src/**/*.js',
    './public/index.html',
  ],

  // Include any special characters you're using in this regular expression
  defaultExtractor: content => content.match(/[A-Za-z0-9-_:/]+/g) || []
})

module.exports = {
  plugins: [
    require('tailwindcss'),
    require('autoprefixer'),
    ...process.env.NODE_ENV === 'production'
      ? [purgecss]
      : []
  ]
}

The content property in the PurgeCSS plugin takes an array of file globs that it should check for the inclusion of CSS selectors. In a create-react-app project we want it to check all of our React components so we pass ./src/**/*.js which means check any nested folders inside of src for any file with an extension of .js. We also want it to look at our ./public/index.html file because Tailwind uses Normalize, and without having it check the projects HTML page, it will gut a lot of the Normalize rules that we want it to include.

There are some pitfalls with PurgeCSS, like it won't actually render your components to check dynamic class usage, so you want to avoid partial class names in dynamic renders and instead stick to full class names.

import React from 'react'

// DO NOT DO THIS
function Button({ color, children }) {
  return <button className={`text-${color}`}>{children}</button>
}

const App = () => (
  <Button color="red-300">Do not click me</Button>
)

///////////////////////////////////
// Instead do this!

function Button({ color, children }) {
  return <button className={`${color}`}>{children}</button>
}

const App = () => (
  <Button color="text-red-300">Do not click me</Button>
)

The other thing that we need to do is make a slight modification to one of our scripts in package.json. The addition of NODE_ENV=production to our prebuild script will set the environment variable for Webpack which create-react-app uses under the hood, and will trigger the PostCSS cli to use PurgeCSS in the building of our styles.

// package.json
{
  "scripts": {
  //...
    "prebuild": "NODE_ENV=production yarn build:styles"
  }
}

Now let's build for production, serve our app, open up the dev tools and check out our network tab again.

yarn build && serve -s build

Much better!

If you want to further slim the build there is great documentation on how to control the size of Tailwind.


So now you know how to set up Tailwind in your create-react-app projects and how to get some decent production wins with PurgeCSS + PostCSS. Let me know if you any questions in the comments, or if you enjoyed this article.

Discussion

pic
Editor guide
Collapse
peoray profile image
Emmanuel Raymond

Great article, just one small error I noticed.
In your package.json file:
You omitted the src path for tailwind.css and this will cause an error when starting the app
Instead of this:
"build:styles": "postcss tailwind.css -o src/styles.css",
Do this:
"build:styles": "postcss src/tailwind.css -o src/styles.css",

This shoud be corrected in the article :)

Collapse
hagnerd profile image
Matt Hagner Author

Very good catch :D

Collapse
gokatz profile image
Gokul Kathirvel

TIL: pre<scriptname> and post<scriptname>. Thanks for the post @hagnerd

Collapse
iwilsonq profile image
Ian Wilson

nice post matt! Loving tailwind.

etting you know there a typo on this line
yarn add -D tailwdincss ...

Collapse
hagnerd profile image
Matt Hagner Author

🤦‍♂️ Thanks! Fixing now.

Collapse
parsa_morshed profile image
Parsa Morshed

===I did everything except adding these:

"scripts": {
//... place these after the four scripts created by CRA
"build:styles": "postcss tailwind.css -o src/styles.css",
"prebuild": "npm run build:styles",
"prestart": "npm run build:styles"
}

===If I do just that then nothing works. Shows plain html
===But
===If I do add those scripts, and do npm start I get this error:

Input Error: You must pass a valid list of files to parse
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! setting-up-tailwind@0.1.0 build:styles: postcss tailwind.css -o src/styles.css
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the setting-up-tailwind@0.1.0 build:styles script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! C:\tmp\nodejs\npm-cache_logs\2019-10-17T19_50_22_634Z-debug.log
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! setting-up-tailwind@0.1.0 prestart: npm run build:styles
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the setting-up-tailwind@0.1.0 prestart script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR! C:\tmp\nodejs\npm-cache_logs\2019-10-17T19_50_24_096Z-debug.log

===I've tried this multiple times over and over and the same keeps happening

Collapse
janisii profile image
janisii

The error says that tailwind.css could not be found. There should be src/ before tailwind.css.

"scripts": {
//... place these after the four scripts created by CRA
"build:styles": "postcss src/tailwind.css -o src/styles.css",
"prebuild": "NODE_ENV=production npm run build:styles",
"prestart": "npm run build:styles"
},

Collapse
hagnerd profile image
Matt Hagner Author

Hey Parsa, do you mind throwing up a github repo with the issue you are having? It would help me locate what the issue might be.

Collapse
joshcasbolt_8 profile image
Josh Casbolt

Hey buddy,

check your package.json and double check you are doing npm run in all cases

I had this exact error and replaced yarn with NPM without thinking so was doing npm build:styles :facepalm:

Collapse
miftahafina profile image
Miftah Afina

Thanks

Collapse
mwarapitiya profile image
Malindu Warapitiya

@apply is not working inside component styles.css. Is there a workaround for this?

Collapse
hagnerd profile image
Matt Hagner Author

Classes set up with apply should be created in the tailwind.css file after the @tailwind components; and before the @tailwind utilities;

Read more about it on the Tailwind docs.

Collapse
muqsithck profile image
Abdul Muqsith

it shows the following error.

yarn run v1.19.2
$ yarn build:styles
$ postcss tailwind.css -o src/styles.css
Input Error: You must pass a valid list of files to parse
error Command failed with exit code 1.
info Visit yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed with exit code 1.
info Visit yarnpkg.com/en/docs/cli/run for documentation about this command.

Collapse
raulismasiukas profile image
Raulis Masiukas

Thanks for the great article :)

Collapse
hunterheston profile image
Hunter Heston

Thanks a lot! Great post very clear and helpful.

Collapse
stiv_ml profile image
Stiv Marcano

Dude this post is amazing, helped me get up and running in minutes. Kudos!

Collapse
themasix profile image
TheMasix

Great Post! Thanks.
But what about watching tailwind.css to rebuild it on change?

Collapse
hagnerd profile image
Matt Hagner Author

That's a good question. I don't find myself frequently changing the tailwind.css file. You can pass a -w flag to postcss-cli in this case.

Collapse
shivamd20 profile image
Collapse
heyfirst profile image
First Kanisorn Sutham

Great! This one is good tutorial