How to svg sprites vs icon fonts — which is better in 2026?
- Step 1Audit your current icon font load — Open the network tab and find the
.woff2request(s). Note the transfer size and count how many distinct icons you actually render versus how many glyphs the font ships. Most projects load 1,000+ glyphs to use 15–40. - Step 2Export just the icons you use as SVG — Download SVG versions of only your used icons from the library's site (Font Awesome, Material Symbols, etc. all offer per-icon SVG download). Name each file by its eventual ID, e.g.
cart.svg,magnifying-glass.svg— the file name becomes the symbol ID stem. - Step 3Normalise colour handling — If you want CSS to recolour icons the way font
colordid, set the icon fills tocurrentColorfirst with svg-monochrome-converter. Inline sprites withcurrentColorthen inherit text colour exactly like an icon font did. - Step 4Combine into one sprite — Drop all the SVGs onto the Sprite Builder, set a prefix like
icon, and downloadsprite.svg. Paste it once near the top of<body>— this is your replacement for the font@font-faceblock. - Step 5Replace classes with use references — Swap each
<i class="fa fa-cart"></i>for<svg width="1em" height="1em" aria-hidden="true"><use href="#icon-cart"/></svg>. Using1emfor size makes the icon track surrounding font-size, mirroring how icon-font glyphs sized themselves. - Step 6Delete the font and verify — Remove the font's
<link>/@font-faceand its CSS. Re-test the page: confirm icons render, recolour viacurrentColor, and that screen readers announce only labelled icons (decorative ones carryaria-hidden).
SVG sprites vs icon fonts, dimension by dimension
Practical 2026 comparison. 'Webfont' assumes a typical icon-font library (Font Awesome / Material Icons).
| Dimension | Icon font (webfont) | SVG `<symbol>` sprite |
|---|---|---|
| Payload model | Whole font downloads even if you use 1 glyph (~150 KB typical full set) | Only included icons ship — a 20-icon sprite is a few KB |
| Colours | Single fill colour per glyph (font is one channel) | Multiple fills, strokes, gradients per icon |
| Recolour via CSS | color: … on the element | currentColor icons inherit color; hardcoded fills need CSS vars or edits |
| Accessibility | Screen readers may read the private-use Unicode char; needs aria-hidden hacks | ARIA on the outer <svg>; decorative icons get aria-hidden="true" |
| Rendering | Hinted glyph — can blur on fractional pixel grids | True vector — crisp at any size |
| Load flash | FOIT/FOUT possible while font downloads | None — sprite is inline markup |
| Build step | Drop in CDN link | Combine SVGs into a sprite (the Sprite Builder does this) |
Migration mapping: font class → use reference
Mechanical replacements once the sprite is inlined. Prefix icon, file names match the icon's eventual ID.
| Before (icon font) | Source file | After (SVG sprite) |
|---|---|---|
<i class="fa fa-cart"></i> | cart.svg | <svg width="1em" height="1em" aria-hidden="true"><use href="#icon-cart"/></svg> |
<i class="fa fa-search"></i> | search.svg | <use href="#icon-search"/> (inside a sized <svg>) |
<span class="material-icons">menu</span> | menu.svg | <use href="#icon-menu"/> |
Font color: red recolour | icon with fill="currentColor" | <svg style="color:red"><use href="#icon-..."/></svg> |
Cookbook
End-to-end migration snippets from a real Font Awesome → SVG sprite swap.
Before: icon font markup and load
The starting point — a CDN font link and class-based icons. The whole font downloads regardless of how few icons the page uses.
<head> <link rel="stylesheet" href="https://…/font-awesome.min.css"> <!-- pulls ~150KB woff2 of glyphs --> </head> <body> <i class="fa fa-cart"></i> <i class="fa fa-search"></i> <i class="fa fa-bars"></i> </body>
Prep: make icons currentColor-aware
To keep CSS recolouring behaviour, convert each icon's fills to currentColor before combining. Then the inline icon inherits the text color, just like a font glyph.
Each cart.svg / search.svg / menu.svg → svg-monochrome-converter (mode: single, then edit fill to currentColor) OR set fill="currentColor" directly in the source Result per icon: <svg viewBox="0 0 24 24"><path fill="currentColor" d="…"/></svg>
Build: combine into one sprite
Drop the prepared icons onto the Sprite Builder with prefix 'icon'. The output is a single inline sprite to paste once.
Inputs: cart.svg, search.svg, menu.svg Prefix: icon sprite.svg (paste once, top of <body>): <svg xmlns="http://www.w3.org/2000/svg" style="display:none"> <symbol id="icon-cart" viewBox="0 0 24 24"><path fill="currentColor" d="…"/></symbol> <symbol id="icon-search" viewBox="0 0 24 24"><path fill="currentColor" d="…"/></symbol> <symbol id="icon-menu" viewBox="0 0 24 24"><path fill="currentColor" d="…"/></symbol> </svg>
After: use references that track color
The final markup. Sizing with 1em tracks font-size; currentColor + CSS color recolours exactly like the old font did.
.icon { width: 1em; height: 1em; }
.danger { color: #dc2626; }
<svg class="icon" aria-hidden="true"><use href="#icon-cart"/></svg>
<svg class="icon danger" aria-hidden="true"><use href="#icon-search"/></svg>
<button aria-label="Open menu">
<svg class="icon" aria-hidden="true"><use href="#icon-menu"/></svg>
</button>Size comparison for a real 24-icon set
Illustrative numbers showing why ship-only-what-you-use wins. Both compress on the wire; the structural win is not loading 1,000+ unused glyphs.
Icon font (full woff2): ~150 KB downloaded, 15 icons used SVG sprite (24 icons): ~6-12 KB uncompressed → ~2-4 KB gzipped Net: the font ships ~10x the bytes for the same visible result, and can't do multi-color or per-icon gradients.
Edge cases and what actually happens
Icon-font color recolour stops working after migration
ExpectedFont glyphs recolour via color because they ARE text. SVG icons only inherit color when their fills are currentColor. If you migrated icons with hardcoded fill="#333", CSS color won't touch them. Convert fills to currentColor first (svg-monochrome-converter) or expose CSS variables.
Two migrated icons share an internal gradient id
ID clashFont glyphs have no internal IDs, but SVGs do. The Sprite Builder does not namespace internal ids, so two icons that both define id="a" for a gradient will collide once combined. Rename internal IDs per-icon before building, or stick to single-colour currentColor icons during migration.
Icon size no longer scales with text
Sizing changeFont icons inherited font-size automatically. SVG <use> does not — set width/height. Use 1em to mimic font behaviour (icon tracks surrounding text size). Omitting dimensions collapses the icon to 0×0 and it disappears.
Screen reader still announces something odd
Needs ARIAIcon fonts often required aria-hidden to silence the glyph character. SVG icons are silent by default but the builder adds no ARIA. For decorative icons add aria-hidden="true"; for meaningful ones add role="img" + aria-label. The migration is your chance to fix labels that were broken under the font.
You exported the whole font as one giant SVG
Wrong granularitySome font exporters produce a single multi-glyph SVG. Combining that as one symbol defeats the point. Export icons individually (one file per icon) so each becomes its own <symbol>. The builder makes one symbol per file — it doesn't split a multi-glyph file.
Ligature-based icon usage (Material Icons text)
No direct mapping<span class="material-icons">menu</span> relied on font ligatures — there is no ligature equivalent in SVG. Each ligature name maps to a downloaded SVG file and a <use> reference. It's a find-and-replace, but it is per-icon, not automatic.
Expecting the builder to subset or tree-shake icons
Not its jobThe builder includes exactly the files you give it — there is no usage analysis or dead-icon removal. 'Ship only what you use' is achieved by you selecting only used icons. For automated discovery from a glob, wire the runner into your bundler (see the automation guide).
Multi-weight icon font (e.g. solid + regular)
Separate symbolsA font's weight variants become distinct SVG files: cart-solid.svg and cart-regular.svg → icon-cart-solid and icon-cart-regular. There's no 'weight' concept in a sprite; each variant is its own symbol you reference explicitly.
Frequently asked questions
Are icon fonts still acceptable in 2026?
They work, but they're no longer recommended for new projects. They load far more than you use, can't do multi-colour, blur on fractional grids, and carry accessibility baggage. SVG <symbol> sprites are better on payload, colour, rendering, and ARIA. The one thing fonts had — zero build step — is solved by a one-click Sprite Builder.
Can SVG sprites do multi-colour icons?
Yes — that's a core advantage. Each path inside a <symbol> can carry its own fill, stroke, or gradient, none of which a single-channel font glyph can express. The Sprite Builder copies that markup verbatim, so multi-colour icons survive combination intact (just avoid internal-ID clashes between icons).
How do I recolour sprite icons with CSS like I did with the font?
Set the icon's fills to currentColor, then CSS color on the outer <svg> (or an ancestor) recolours it exactly as the font color did. For multi-colour icons, expose fills as CSS custom properties using svg-css-variable-injector and override them per-context.
What's the real file-size difference?
A full icon-font woff2 is typically ~150 KB and downloads even for a single glyph. A sprite of the 20–40 icons you actually use is a few KB uncompressed and gzips to a couple KB. Both compress on the wire, but the structural win is not shipping 1,000+ unused glyphs.
Does migrating change how I size icons?
Yes. Font glyphs sized themselves from font-size; SVG <use> needs explicit width/height. Use 1em to reproduce font-like behaviour where the icon tracks surrounding text size. Forgetting dimensions is the #1 reason a migrated icon 'disappears' — it collapsed to 0×0.
Will the Sprite Builder remove icons I don't use?
No. It combines exactly the files you upload — there's no tree-shaking or usage analysis. The way you 'ship only what you use' is by selecting only the icons you use. For automatic discovery across a codebase, use a bundler plugin and dispatch via the runner (see the automation guide).
Can I keep using xlink:href like old font polyfills suggested?
No need. xlink:href is deprecated; modern browsers use plain href. Icon fonts often needed a polyfill (svg4everybody) for old IE — irrelevant in 2026. Use <use href="#id"> and don't carry the legacy attribute.
Do I lose hover/active colour states moving off the font?
No — you gain control. With currentColor icons, a:hover .icon { color: … } works just like text. With multi-colour icons exposed as CSS vars, you can animate individual parts on hover, which icon fonts could never do.
How do I handle a multi-weight font like solid vs regular?
Export each weight as a separate SVG and give them distinct names (star-solid.svg, star-regular.svg). Each becomes its own symbol (icon-star-solid, icon-star-regular). There's no weight switch in a sprite — you reference the variant you want explicitly.
Is the sprite better for accessibility out of the box?
It's better-positioned, not automatic. The builder adds no ARIA, so you still add aria-hidden="true" for decorative icons and role="img" + aria-label for meaningful ones. The advantage over fonts is that you're not fighting a glyph character the screen reader wants to announce.
Can I do the whole migration without leaving the browser?
Yes. Export per-icon SVGs, optionally run them through svg-monochrome-converter for currentColor, combine in the Sprite Builder, then find-and-replace classes with <use>. Nothing uploads — processing is in-browser, and the public API never accepts file content.
What about emails — can I use a sprite there?
No. Email clients strip <svg>/<use> (and most strip embedded fonts too). For email, use rasterised PNG icons. This isn't a sprite-vs-font question — neither vector approach works in email; the sprite is for the web.
Privacy first
Every JAD SVG tool runs entirely in your browser using the DOM API and Canvas. Your SVG files never leave your device — verified by zero outbound network requests during processing.