Controlling File Size

Strategies for keeping your generated CSS small and performant.

Using the default configuration, Tailwind CSS comes in at 58.1kb minified and gzipped.

Here are a few other popular frameworks for comparison:

Framework Original Size Minified Gzip Brotli
Tailwind 783.5kb 603.3kb 78.0kb 22.6kb
Bootstrap 187.8kb 152.1kb 22.7kb 16.7kb
Bulma 224.2kb 189.9kb 24.9kb 19.1kb
Foundation 154.1kb 119.2kb 15.9kb 12.9kb
Tachyons 111.7kb 71.8kb 13.4kb 7.5kb
Semantic UI 809.4kb 613.8kb 100.6kb 77.8kb
Materialize 175.0kb 138.5kb 21.1kb 17.1kb

By comparison Tailwind definitely seems on the heavy side (although if you use Brotli instead of gzip it's still very reasonable), but there are a number of strategies you can use to reduce this file size dramatically.


Removing unused CSS

Mozilla's Firefox Send is built with Tailwind, yet somehow their CSS is only 13.1kb minified, and only 4.7kb gzipped! How?

They're using Purgecss, a tool for removing CSS that you're not actually using in your project. Purgecss is particularly effective with Tailwind because Tailwind generates thousands of utility classes for you, most of which you probably won't actually use.

For example, Tailwind generates margin utilities for every size in your spacing scale, for every side of an element you might want to apply margin to, at every breakpoint you are using in your project. This leads to hundreds of different combinations that are all important to have available, but not all likely to be needed.

When using Purgecss with Tailwind, it's very hard to end up with more than 10kb of compressed CSS.

Setting up Purgecss

In the future we may incorporate Purgecss directly into Tailwind, but for now the best way to use it in your project is as a PostCSS plugin.

To get started with Purgecss, first install @fullhuman/postcss-purgecss:

# Using npm
npm install @fullhuman/postcss-purgecss --save-dev

# Using yarn
yarn add @fullhuman/postcss-purgecss -D

Next, add it as the last plugin in your postcss.config.js file:

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

  // Specify the paths to all of the template files in your project 
  content: [
    './src/**/*.html',
    './src/**/*.vue',
    './src/**/*.jsx',
    // etc.
  ],

  // Include any special characters you're using in this regular expression
  defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
})

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

Note that in this example, we're only enabling Purgecss in production. We recommend configuring Purgecss this way because it can be slow to run, and during development it's nice to have every class available so you don't need to wait for a rebuild every time you change some HTML.

Finally, we recommend only applying Purgecss to Tailwind's utility classes, and not to base styles or component classes. The easiest way to do this is to use Purgecss's whitelisting feature to disable Purgecss for non-utility classes:

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

@tailwind  utilities;

This will ensure you don't accidentally purge important base styles when working with frameworks like Next.js, Nuxt, vue-cli, create-react-app, and others that hide their base HTML template somewhere in your node_modules directory.

Writing purgeable HTML

Purgecss uses "extractors" to determine what strings in your templates are classes. In the example above, we use a custom extractor that will find all of the classes Tailwind generates by default:

const purgecss = require('@fullhuman/postcss-purgecss')({
  // ...
  defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
})

The way it works is intentionally very naive. It doesn't try to parse your HTML and look for class attributes or dynamically execute your JavaScript — it simply looks for any strings in the entire file that match this regular expression:

/[\w-:/]+(?<!:)/g

That means that it is important to avoid dynamically creating class strings in your templates with string concatenation, otherwise Purgecss won't know to preserve those classes.

Don't use string concatenation to create class names

<div :class="text-{{ error ? 'red' : 'green' }}-600"></div>

Do dynamically select a complete class name

<div :class="{{ error ? 'text-red-600' : 'text-green-600' }}"></div>

As long as a class name appears in your template in its entirety, Purgecss will not remove it.

Understanding the regex

The /[\w-/:]+(?<!:)/g regular expression we recommend as a starting point matches all of the non-standard characters Tailwind uses by default, like : and /.

It also uses a negative lookbehind to make sure that if a string ends in :, the : is not considered part of the string. This is to ensure compatibility with the class object syntax supported by Vue and the Classnames library for React:

<!-- Match `hidden`, not `hidden:` -->
<div :class="{ hidden: !isOpen, ... }"><!-- ... --></div>

It's important to note that because of the negative lookbehind in this regex, it's only compatible with Node.js 9.11.2 and above. If you need to use an older version of Node.js to build your assets, you can use this regular expression instead:

- /[\w-/:]+(?<!:)/g
+ /[\w-/:]*[\w-/:]/g

Customizing the regex

If you're using any other special characters in your class names, make sure to update the regular expression to include those as well.

For example, if you have customized Tailwind to create classes like w-50%, you'll want to add % to the regular expression:

- /[\w-/:]+(?<!:)/g
+ /[\w-/:%]+(?<!:)/g

Removing unused theme values

If you can't use Purgecss for one reason or another, you can also reduce Tailwind's footprint by removing unused values from your configuration file.

The default theme provides a very generous set of colors, breakpoints, sizes, margins, etc. to make sure that when you pull Tailwind down to prototype something, create a CodePen demo, or just try out the workflow, the experience is as enjoyable and fluid as possible.

We don't want you to have to go and write new CSS because we didn't provide enough padding helpers out of the box, or because you wanted to use an orange color scheme for your demo and we only gave you blue.

This comes with a trade-off though: the default build is significantly heavier than it would be on a project with a purpose-built configuration file.

Here are a few strategies you can use to keep your generated CSS small and performant.

Limiting your color palette

The default theme includes a whopping 93 colors used for backgrounds, borders, text, and placeholders, all of which also have hover: and focus variants, as well as responsive variants at the five default screen sizes.

This means that by default, there are 5580 classes generated from this color palette out of 12,230 classes total in the entire default build.

Very few projects actually need this many colors, and removing colors you don't need can have a huge impact on the overall file size.

Here's how using a smaller color palette affects the final size:

Colors Original Minified Gzip Brotli
93 (default) 783.5kb │ 603.3kb │ 78.0kb │ 22.6kb
50 530.2kb │ 399.3kb │ 56.6kb │ 17.5kb
25 381.6kb │ 279.5kb │ 44.1kb │ 14.0kb

Removing unused breakpoints

Since almost every Tailwind utility is copied for every screen size, using fewer screen sizes can have a huge impact on the overall file size as well.

Here's how defining fewer screens affects the output:

Breakpoints Original Minified Gzip Brotli
4 (default) 783.5kb │ 603.3kb │ 78.0kb │ 22.6kb
3 624.0kb │ 481.3kb │ 62.7kb │ 20.8kb
2 464.6kb │ 359.4kb │ 47.4kb │ 19.2kb
1 305.1kb │ 237.5kb │ 32.1kb │ 17.6kb

If you only need 3 screen sizes and 35 colors, you're down to 39.5kb after gzip (14.6kb after Brotli!) without changing anything else.

Disabling unused utilities and variants

If you don't expect to need a certain utility plugin in your project at all, you can disable it completely by setting it to false in the corePlugins section of your config file:

// tailwind.config.js
module.exports = {
  // ...
  corePlugins: {
    float: false
  }
}

If you need a utility but don't need the responsive versions, set its variants to an empty array to generate 80% fewer classes:

module.exports = {
  // ...
  variants: {
    appearance: []
  }
}

These are mostly small wins compared to limiting your color palette or using fewer breakpoints, but they can still add up.