Font Awesome is an incredibly popular icon library. Unfortunately, it’s somewhat easy to use in a way that results in less-than-ideal performance. By subsetting Font Awesome, we can remove any unused glyphs from the font files it provides. This will reduce the number of bytes transmitted over the wire, and improve performance.
Let’s subset fonts together in a Font Awesome project to see the difference it makes. As we go, I’ll assume you’re importing the CSS file Font Awesome provides, and using its web fonts to display icons.
Let’s set things up
For the sake of demonstration, I have nothing but an HTML file that imports Font Awesome’s base CSS file. To get a reasonable sample of icons, I’ve listed out each one that I use on one of my side projects.
Here’s what our HTML file looks like in the browser before subsetting fonts:
Here’s a look at DevTool’s Network tab to see what’s coming down.
Now let’s see how many bytes our font files take to render all that.
Here’s our base case
We want to see what the most straightforward, least performant use of Font Awesome looks like. In other words, we want the slowest possible implementation with no optimization. I’m importing the all.min.css
file Font Awesome provides.
As we saw above, the gzipped file weighs in at 33.4KB, which isn’t bad at all. Unfortunately, when we peek into DevTool’s Font tab, things get a little worse.
While font files are not as expensive a resource for your browser to handle as JavaScript, those are still bytes your browser needs to pull down, just for some little icons. Consider that some of your users might be browsing your site on mobile, away from a strong or fast internet connection.
First attempt using PurifyCSS
Font Awesome’s main stylesheet contains definitions for literally thousands of icons. But what if we only need a few dozen at most? Surely we could trim out the unneeded stuff?
There are many tools out there that will analyze your code, and remove unused styles from a stylesheet. I happen to be using PurifyCSS. While this library isn’t actively maintained anymore, the idea is the same, and in the end, this isn’t the solution we’re looking for. But let’s see what happens when we trim our CSS down to only what’s needed, which we can do with this script:
const purify = require("purify-css");
const content = ["./dist/**/*.js"]; // Vite-built content
purify(content, ["./css/fontawesome/css/all.css"], {
minify: true,
output: "./css/fontawesome/css/font-awesome-minimal-build.css"
});
And when we load that newly built CSS file, our CSS bytes over the wire drop quite a bit, from 33KB to just 7.1KB!
But unfortunately, our other Font Awesome font files are unchanged.
What happened? PurifyCSS did its job. It indeed removed the CSS rules for all the unused icons. Unfortunately, it wasn’t capable of reaching into the actual font files to trim down the glyphs, in addition to the CSS rules.
If only there was a tool like PurifyCSS that handles font files…
Subsetters to the rescue!
There are, of course, tools that are capable of removing unused content from font files, and they’re called subsetters. A subsetter analyzes your webpage, looks at your font files, and trims out the unused characters. There are a bunch of tools for subsetting fonts out there, like Zach Leatherman’s Glyphhanger. As it turns out, subsetting Font Awesome is pretty straightforward because it ships its own built-in subsetters. Let’s take a look.
Subsetting fonts automatically
The auto subsetting and manual subsetting tools I’m about to show you require a paid Font Awesome Pro subscription.
Font Awesome allows you to set up what it calls kits, which are described in the Font Awesome docs as a “knapsack that carries all the icons and awesomeness you need in a neat little lightweight bundle you can sling on the back of your project with ease.” So, rather than importing any and every CSS file, a kit gives you a single script tag you can add to your HTML file’s <head>
, and from there, the kit only sends down the font glyphs you actually need from the font file.
Creating a kit takes about a minute. You’re handed script tag that looks something like this:
<script src="https://kit.fontawesome.com/xyzabc.js" crossorigin="anonymous"></script>
When the script loads, we now have no CSS files at all, and the JavaScript file is a mere 4KB. Let’s look again at the DevTools Fonts tab to see which font files are loaded now that we’ve done some subsetting.
We’ve gone from 757KB down to 331KB. That’s a more than 50% reduction. But we can still do better than that, especially if all we’re rendering is 54 icons. That’s where Font Awesome’s manual font subsetter comes into play.
Subsetting fonts manually
Wouldn’t it be nice if Font Awesome gave us a tool to literally pick the exact icons we wanted, and then provide a custom build for that? Well, they do. They don’t advertise this too loudly for some reason, but they actually have a desktop application exactly for subsetting fonts manually. The app is available to download from their site — but, like the automatic subsetter, this app requires a paid Font Awesome subscription to actually use.
Search the icons, choose the family, add what you want, and then click the big blue Build button. That’s really all it takes to generate a custom subset of Font Awesome icons.
Once you hit the button, Font Awesome will ask where it should save your custom build, then it dumps a ZIP file that contains everything you need. In fact, the structure you’ll get is exactly the same as the normal Font Awesome download, which makes things especially simple. And naturally, it lets you save the custom build as a project file so you can open it back up later to add or remove icons as needed.
We’ll open up DevTools to see the final size of the icons we’re loading, but first, let’s look at the actual font files themselves. The custom build creates many different types, depending on what your browser uses. Let’s focus on the .woff2
files, which is what Chrome loads. The same light, regular, duotone, solid, and brand files that were there before are still in place, except this time no file is larger than 5KB… and that’s before they’re gzipped!
And what about the CSS file? It slims down to just 8KB. With gzip, it’s only 2KB!
Here’s the final tally in DevTools:
Before we go, take a quick peek at those font filenames. The fa-light-300.woff2
font file is still there, but the others look different. That’s because I’m using Vite here, and it decided to automatically inline the font files into the CSS, since they’re so tiny.
That’s why our CSS file looks a little bigger in the DevTools Network tab than the 2KB we saw before on disk. The tradeoff is that most of those font “files” from above aren’t files at all, but rather Base64-encoded strings embedded right in this CSS file, saving us additional network requests.
All that said, Vite is inlining many different font formats that the browser will never use. But overall it’s a pretty small number of bytes, especially compared to what we were seeing before.
Before leaving, if you’re wondering whether that desktop font subsetting GUI tool comes in a CLI that can integrate with CI/CD to generate these files at build time, the answer is… not yet. I emailed the Font Awesome folks, and they said something is planned. That’ll allow users to streamline their build process if and when it ships.
As you’ve seen, using something like Font Awesome for icons is super cool. But the default usage might not always be the best approach for your project. To get the smallest file size possible, subsetting fonts is something we can do to trim what we don’t need, and only serve what we do. That’s the kind of performance we want, especially when it comes to loading fonts, which have traditionally been tough to wrangle.
I extensively use icomoon to generate my fonts. I add the required icons and generate fonts. The resulting style will have ttf and woff. I take the woff, convert to woff2 and then in my css, I use woff2 with woff as fallback.
I’m never sure how to interpret performance data like these.
You say a ‘kit’ reduces the size of the font-resources by more than 50%. But if I look at the ‘DomContentLoaded’ value in both screenshots it appears it took 28% longer to load…
I recently switched to using kits for a number of projects, but seeing this makes me wonder if that was actually a good idea..
Do I kinda really have to use the manual font subsetting? Or am I putting too much weight on this one figure?
You are not wrong, kinda. He really fetch those files faster, but it is most likely, because he did it in localhost. While kit had to be fetch from server. So ye in that case it looks much slower, but once deployed to the world, it should be faster.
Obviously what he tried to point out is, that in first case he had to download 757KB, while with kit only 331KB. If in both cases those files were on one place, I believe you can imagine what would be faster and why.
I just integrated the fontawesome-subset library into my design system. I’m able to define a json file that contains a list of the icons I want, and the library generates tiny font files. I find this superior because my team doesn’t need a Pro subscription, and font generation is relatively programmatic.
Caveats: it doesn’t touch CSS, so there’s a second map in SCSS to maintain. It also doesn’t (yet) work with v6.
Font icons present numerous problems, including accessibility issues. Fontawesome recommends you use their JS + SVG method over the font icon method. It offers control over the icon appearance that isn’t available with the webfont method.
The subsetter described in the article works for JS + SVG. You can also edit the JavaScript files directly to delete the unused definitions.
There are many articles on why you shouldn’t use icon fonts. Here’s one: https://www.irigoyen.dev/blog/2021/02/17/stop-using-icon-fonts/
The arguments presented in that article can all be worked around, specifically the screen reader one just depends on how you implement them. There is a lot of misinformation out there on icon fonts. They are still a valid and performant way to display icons.