Tired of shipping 800KB+ of Font Awesome fonts when you only use a handful of icons? Here's a tool that automatically detects which icons you're actually using and creates tiny subset fonts.
The Problem
Font Awesome is brilliant, but it comes with a performance cost. When you include the full FA webfonts, browsers download the entire font file even if you only use a few icons. That means:
- FA6 Solid: ~345KB
- FA6 Duotone: ~458KB
- Total payload: Often over 1MB including CSS
Your users pay this cost on every page load, especially painful on mobile connections.
The Solution: fa-subset
I built fa-subset to solve this exact problem. It's a Python tool that:
-
Scans your codebase to find all
fa-*
classes you actually use - Detects Font Awesome styles (solid, duotone, brands, etc.) automatically
- Creates tiny subset fonts containing only the icons you need
- Generates optimised CSS that references the subset fonts
Real Results
Here's what happened on a production site:
Before: 803 KB fonts + CSS (~1.29 MB total)
After: 14.5 KB fonts + CSS (~0.50 MB total)
Savings: 98.2% reduction in font size, 61% total payload reduction
Key Features
✅ Works with FA5 & FA6 (all styles: solid, regular, light, thin, duotone, brands, sharp)
✅ Handles complex selectors like .fa-duotone.fa-house:after
✅ Duotone support with proper dual codepoint handling
✅ Multi-pack subsetting (separate optimised fonts per style)
✅ Dry-run reports with detailed breakdown
✅ Works with minified CSS and any file extensions
Quick Start
Prerequisites
pip install fonttools
Basic Usage
- Dry run to see what will be optimised:
python3 bin/fa-subset.py \
--src ./src ./templates \
--ext .html .php .twig .js \
--pack solid:css=./css/fontawesome.min.css,font=./fonts/fa-solid-900.ttf \
--pack duotone:css=./css/duotone.min.css,font=./fonts/fa-duotone-900.ttf \
--out ./optimised \
--dry-run
-
Generate subset fonts (remove
--dry-run
):
python3 bin/fa-subset.py \
--src ./src ./templates \
--ext .html .php .twig .js \
--pack solid:css=./css/fontawesome.min.css,font=./fonts/fa-solid-900.ttf \
--pack duotone:css=./css/duotone.min.css,font=./fonts/fa-duotone-900.ttf \
--out ./optimised
- Include the optimised CSS:
<link rel="stylesheet" href="/optimised/solid/fontawesome.solid.subset.min.css">
<link rel="stylesheet" href="/optimised/duotone/fontawesome.duotone.subset.min.css">
Smart Detection
The tool intelligently handles:
Mixed content:
<i class="fa-solid fa-house"></i> <!-- Solid pack -->
<i class="fa-duotone fa-user"></i> <!-- Duotone pack -->
<i class="fas fa-search"></i> <!-- FA5 alias → solid -->
<span class="fa-brands fa-github"></span> <!-- Brands pack -->
Complex selectors in CSS:
.fa-duotone.fa-house:after { content: "\f015\f015"; } /* Duotone dual codepoints */
.custom-icon.fa-solid.fa-star { /* Custom styling */ }
Dynamic content with proper fallbacks:
// The tool finds icons even in JS template strings
const iconHtml = `<i class="fa-solid fa-${iconName}"></i>`;
Advanced Features
JSON Reports for CI/CD
--report-json ./report.json --strict-styles
Perfect for CI pipelines to catch style conflicts:
{
"conflicts": [
{"file": "nav.html", "line": 42, "icon": "user", "styles": ["solid", "duotone"]}
],
"packs": {
"solid": {"icons_found": ["house", "user"], "codepoints": ["U+F015", "U+F007"]}
}
}
Composer Integration
Add these scripts to your composer.json
:
{
"scripts": {
"fa-analyse": "python3 bin/fa-subset.py --src ./src --pack solid:css=./css/fontawesome.min.css,font=./fonts/fa-solid-900.ttf --out ./optimised --dry-run",
"fa-optimise": "python3 bin/fa-subset.py --src ./src --pack solid:css=./css/fontawesome.min.css,font=./fonts/fa-solid-900.ttf --out ./optimised"
}
}
Then simply run:
composer fa-analyse # See what will be optimised
composer fa-optimise # Generate the subset fonts
Performance Impact
The difference is dramatic:
Before (Full FA6):
- First Contentful Paint: slower due to font loading
- Lighthouse Performance Score: penalised for large resources
- Mobile users: significant bandwidth usage
After (Subset):
- Faster initial page loads
- Better Lighthouse scores
- Happier mobile users
- Reduced CDN costs
Best Practices
-
Cache aggressively: Serve subset fonts with long
Cache-Control
headers - Use compression: Enable Brotli/gzip on your server
-
One style per icon: Don't mix
fa-solid
andfa-duotone
on the same element -
Run in CI: Use
--strict-styles
to catch conflicts early
Common Use Cases
WordPress themes:
--src ./wp-content/themes/mytheme --ext .php
React/Vue projects:
--src ./src --ext .js .jsx .vue .tsx
Laravel applications:
--src ./resources/views ./app --ext .blade.php .php
Troubleshooting
Icons show as squares?
- Ensure you're including the subset CSS, not the original
- Check the Network tab - the subset .woff2 should load
- Verify CORS headers if serving from a CDN
"No icon rules found in CSS"?
- Use
all.min.css
or per-style CSS files (e.g.,solid.min.css
) - The CSS must contain icon mappings, not just variables
Why This Matters
Font optimisation often gets overlooked, but it's one of the highest-impact performance wins you can achieve. With fa-subset, you get:
- Massive file size reductions (90%+ typical)
- Faster page loads especially on mobile
- Better Core Web Vitals scores
- Reduced bandwidth costs
- Zero maintenance once set up
Get Started
Repository: fa-subset on GitHub
Licence: BSD-2-Clause (bring your own FA licence)
The tool includes comprehensive documentation, examples, and CI integration guides. Give it a try on your next project - your users (and Lighthouse scores) will thank you!
Made by Sam Sheridan at Sheridan Internet. Not affiliated with Font Awesome/Fonticons Inc.
Top comments (0)