Add Tailwind to a Next.js project

13 May 2020

Tailwind CSS is a highly customizable, low-level CSS framework that gives you all of the building blocks you need to build bespoke designs without any annoying opinionated styles you have to fight to override.

Tailwind CSS has completely changed the way I develop web apps thanks to the utility first workflow. And the recent release of v1.4 made it even easier to get a very small production build by including PurgeCSS as a built-in process. If you've been on the fence about trying out this great framework now is the time to jump in.

Create the Next.js app

We'll start by creating a default Create Next App to keep things simple.

npx create-next-app cna-tailwind && cd cna-tailwind

When asked to pick a template simply select Default starter app and hit return to complete the installation of your Next app.

Add Tailwind to the project

Install the required dependencies

Install the development dependencies from our project root.

npm install --save-dev tailwindcss autoprefixer

Tailwind doesn't include any vendor prefixes out of the box and that's why we install autoprefixer.

Create the Tailwind CSS

To keep things clean, I prefer to have a dedicated styles directory within my Tailwind projects. Create a styles directory from your project root (or wherever you like to keep it) and within that create a main.css file.

mkdir -p styles && touch styles/main.css

Next we add the @tailwind directives inside main.css. These directives will be replaced with the actual Tailwind CSS during the build.

@tailwind base;
/* Your own custom base styles */

@tailwind components;
/* Your own custom component styles */

@tailwind utilities;
/* Your own custom utilities */

This main.css file contains all of our project's pre-generated CSS including any of our extra custom CSS. The beauty and strength of using Tailwind is that you won't be required to write much extra CSS though.

Generate the Tailwind configuration

./node_modules/.bin/tailwindcss init

An empty tailwind.config.js file is generated within the project root. This config file allows us to dramatically customise Tailwind and is one of the single biggest strengths of this excellent framework. To read more on how you can configure Tailwind check the official configuration documentation.

Import the CSS into our App

Tailwind will generate a global CSS file for our App. This will require a different setup within Next.js when compared to a component level solution like CSS-in-JS or styled-components. We therefore need to ensure we include main.css in the correct way.

Next.js will throw an error at build time if we try to import the global Tailwind CSS file anywhere other than within pages/_app.js.

Global CSS cannot be imported from files other than your Custom App. Please move all global CSS imports to pages/_app.js.

First step is to create pages/_app.js.

touch pages/_app.js

We aren't doing anything special with the Custom App other than importing Tailwind so the default code will be fine.

import '../styles/main.css';

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}

export default MyApp;

If you're curious you can read more about the Custom App component in the official docs.

Create the PostCSS configuration

Next.js will detect any custom postcss.config.js file in our project's root and use it as part of the build process. This is super convenient as it means we won't need to make any build changes beyond simply creating and populating the config file.

To start, create a postcss.config.js file in the root of your project.

touch postcss.config.js

Populate the config with the following setup.

module.exports = {
  plugins: ['tailwindcss', 'autoprefixer'],
};

We'll be using PostCSS to build our CSS by first running our application's CSS through the Tailwind CLI. This step not only replaces all of the @tailwind directives with the base, components and utilities from Tailwind but also applies your own tailwind.config.js customisations during the build. I cannot emphasise enough just how powerful the customisation options are and the massive amount of time it can save you. So please do spend time reading up on how to customise Tailwind to truly make it your own.

Since Tailwind does not include vendor prefixes, the output of the Tailwind build step is then sent into autoprefixer which will automatically add any vendor prefixes required by our browserlist (or a default set if none is explicitly set in our package.json).

Optimise the production build

Test the build

We aren't actually using Tailwind in our application yet so replace pages/index.js with a simplified version of the index page. Notice that we have now made use of a few of Tailwind's classes such as text-5xl and mx-auto.

import Head from 'next/head';

export default function Home() {
  return (
    <div className="container mx-auto">
      <Head>
        <title>Create Next App</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className="text-center">
        <h1 className="text-5xl font-semibold my-5">
          Welcome to{' '}
          <a href="https://nextjs.org" className="text-blue-600">
            Next.js!
          </a>
        </h1>

        <p className="description">
          Get started by editing{' '}
          <code className="font-mono bg-gray-200 p-1 rounded">
            pages/index.js
          </code>
        </p>
      </main>
    </div>
  );
}

Lets run a quick production build of our Next.js app.

npm run build

Once that has completed, open the generated CSS (it will be in the .next/static/css directory) and look at the styles that Tailwind has generated. That CSS file is absolutely massive and contains the entirety of the framework including tons of styles we haven't used at all!

Unoptimised Tailwind build

This is expected behaviour at this point given our settings though. It's important to note that the development build of Tailwind is large by design ensuring all of the framework's classes are available to you while you build the site. You will only ever use a small percentage of these and Tailwind provides a convenient way to trim that down for your production builds.

Update the Tailwind configuration.

The setup for running PurgeCSS used to be a little more involved requiring us to install extra dependencies and adding a regex pattern to our PostCSS configuration. Thankfully this is now a built-in feature as of version 1.4 and requires nothing more than defining the file patterns that may contain our Tailwind classes.

Replace the contents of the tailwind.config.js file in your project's root to include an array of file patterns.

module.exports = {
  purge: ['./pages/**/*.js'],
  theme: {
    extend: {},
  },
  variants: {},
  plugins: [],
};

Now that we have the purge settings in place, Tailwind will look through those files and treeshake any unused styles from the final production build. We only need to look through JavaScript files within our pages directory in the default app but be sure to include any other locations where your app may be using Tailwind (e.g. ./components/**/*.js) to ensure no classes are purged by accident.

If you're curious, read more about how Tailwind controls the file size for production builds.

Test the optimised Production Build

Now that we have set our purge options, we are ready to run the optimised production build. It will now automatically remove all unused classes from the final build.

npm run build

Optimised Tailwind build

Once that completes open the newly generated production CSS file. You should find it has shrunk down considerably and now only contains the styles we have thus far used in our project. Perfect!

Conclusion

Tailwind CSS truly has had a huge impact on improving my workflow when developing web apps and I cannot imagine starting a new project without first reaching for this framework. Thanks to the helpful conveniences built into Next.js and the advancements in Tailwind v1.4 it has never been quicker or easier to give Tailwind a try.

Share this article
Was this post helpful?
Buy me a coffeeBuy me a coffee
About
I am a London based web developer currently focused on JavaScript, Node and React. Get in touch if you wish to compare eslint configs or debate the deadliest enemy in videogame history (spoiler alert: it was the first goomba).
© 39digits 2020