How to Use PurgeCSS With Hugo
Updated: Jul 22, 2022
Update: As of June 19, 2022, this is now an official PurgeCSS guide, yay.
If you haven’t heard yet, PurgeCSS is awesome. Say you wanna use Bootstrap, but you only need some of the classes? You can do that!
Instead of serving the full CSS file with your site, you can use PurgeCSS to purge the unused classes, which results in a much smaller CSS file. This tutorial shows you how to do that with my favorite static site generator, Hugo.
Minimal, Reproducible Example #
If you get stuck, refer to this GitHub repo. It’s a minimal, reproducible example of this tutorial.
Tooling #
- Install Hugo
- Install Node.js
Write Stats #
In your config.toml
file, add this:
[build]
writeStats = true
Or, If using a config.yaml
file, add this:
build:
writeStats: true
This tells Hugo to write a hugo_stats.json
file to the project root as part of the build. It includes all tags, classes, and ids from your *.html
templates.
Node Packages #
Run this to install the necessary packages:
npm install postcss postcss-cli @fullhuman/postcss-purgecss
If the package.json
file at the project root doesn’t exist yet, this will create it.
If it’s not already there, add node_modules/
to your .gitignore
file.
PostCSS Config File #
Create a postcss.config.js
file at the project root with these contents:
const purgecss = require('@fullhuman/postcss-purgecss')({
content: ['./hugo_stats.json'],
defaultExtractor: content => {
const els = JSON.parse(content).htmlElements;
return [
...(els.tags || []),
...(els.classes || []),
...(els.ids || []),
];
},
safelist: []
});
module.exports = {
plugins: [
...(process.env.HUGO_ENVIRONMENT === 'production' ? [purgecss] : [])
]
};
See the PurgeCSS configuration docs for details on each option.
Note: safelist
is an empty array (for now). Remember, only elements from your HTML templates are extracted. So, if your JS adds elements, you’ll need to safelist them.
HTML Template #
In the HTML Template for your <head>
, add this:
{{ $css := resources.Get "css/bootstrap.css" | resources.PostCSS }}
{{ if hugo.IsProduction }}
{{ $css = $css | minify | fingerprint | resources.PostProcess }}
{{ end }}
<link
rel="stylesheet"
href="{{ $css.RelPermalink }}"
integrity="{{ $css.Data.Integrity }}"
>
This assumes:
- Your CSS file is at
assets/css/bootstrap.css
- You want to minify and fingerprint the production version of this file
If these assumptions aren’t true for you, modify the code accordingly.
Use it #
Cool, now we can use it.
Serve your site in development mode, which is the default:
hugo serve
Open your browser DevTools, go to the Network tab, then note the size of the CSS file.
Now, Control
+ C
to stop it, then serve your site in production mode:
hugo serve --environment production
Notice the CSS file now has a much smaller size.
Environment #
If you don’t want to pass the --environment production
option, you can set this env var:
HUGO_ENVIRONMENT=production
Results #
At the time of this writing, I bundle Bootstrap with some custom styles. When serving locally, the un-minified, un-purged size of the bundle is about 209 kB. The minified, purged size is about 14 kB. It’s even smaller when served from the live site, about 4 kB, because it’s compressed with Brotli.
References #
- PostProcess docs
- Using PurgeCSS with Hugo by Christian Oliff. I stumbled upon his article after writing this one, and I like the way he populates the PurgeCSS
content
array. Check it out