Optimizing Tailwind CSS File Size with PurgeCSS & Laravel Mix

Apr 26, 2020 — 3 mins read

As I was looking for more ways to optimize this blog, I stumbled upon Tailwind's own recommendations regarding shrinking file sizes. The main recommendations are to either disable unused rules (mainly colors) in the Tailwind config or use PurgeCSS to remove any CSS that's not being used. In my case, I was already using Laravel Mix so integrating PurgeCSS was quite simple and it worked really well!

This is a continuation of my quest to get a score of 100 on Google's PageSpeed Insights test, which you can read about here. I currently have consistent 100/100 scores on desktop, but I can't quite get a perfect score on the mobile test.

What is PurgeCSS?

PurgeCSS is a tool to remove unused CSS. It's installed using NPM and can be used with any framework. It's used as part of your development workflow by automating the optimization of your CSS.

Installing PurgeCSS

In this example, I'll be using this Spatie package to make the integration simpler. You can install this package as a dev dependency by using the command below, and since this tutorial assumes you're using Laravel Mix, this will work great!

npm install laravel-mix-purgecss --save-dev

Next, you'll want to add PurgeCSS to your webpack.mix.js file. There are options that can be used to configure PurgeCSS, however we'll use the simplest integration to start. By chaining .purgeCss() onto Mix, we enable PurgeCSS only on production builds. That's perfect for me, for a reason I'll explain below.

Tailwind recommends including all base and component styles by default by modifying the Tailwind imports like so:

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

@tailwind  utilities;

The comments basically ignore checking any CSS between them, in this case the base and components Tailwind imports.

Running a Build

At this point you can use the command below to build your CSS, and see what savings are made. This blog's CSS was reduced from ~900KB to roughly 9KB! That's a 99% decrease, and your savings might be even larger depending on how complex your project is.

npm run production

Running "dev" instead of "production" will result in CSS building without PurgeCSS optimizations.

The Problem with Dynamic Content

Initially I was very pleased with the reduction in file size, but soon realized there was an issue with dynamic content. This blog uses a CMS package called Canvas, which uses HTML elements to separate post content. To see what this page structure looks like, you can view the source of this very post.

The issue was that the styles that I previously created to make posts looks normal were being removed by PurgeCSS. Those rules were not being used by the blog on any static pages and PurgeCSS therefore deemed them unused, when in fact my posts needed them.

PurgeCSS Ignore Comments

To fix this issue, I used the CSS comments below. By putting my post-specific CSS between these comments, PurgeCSS included these selectors regardless of them being found or not in template files. Cool!

/* purgecss start ignore */
This CSS will be included no matter what.
/* purgecss end ignore */

Whitelisting Rules

I also needed to whitelist rules that I was using inside of blog posts that were not in my SASS file. To do this I used the whitelist config option that PurgeCSS provides. You can see all the configuration options here. My webpack.mix.js file now looks like this:

Using PurgeCSS only in Production

I only use PurgeCSS in production simply because I like having the ability to check if PurgeCSS is causing incorrect post formatting. By running a development build, I can rule out PurgeCSS as the culprit, and if it is the culprit I can disable it in production by building for development on my DigitalOcean Droplet. A marginally slower blog is better than a broken blog!

Conclusion

PurgeCSS reduced the size of this blog's CSS file from ~900KB to ~9KB, and roughly halved the page load time in Chrome's Network tab. Although I sometimes continue to battle rules being left out for dynamic content, I've found that writing posts locally and making any necessary fixes to the stylesheets first is the way to go, and worth the hassle!