How to how to optimise svg icon libraries in large design systems
- Step 1Inventory icons that carry <defs> — List the SVGs that actually contain definitions — those are the only ones the purger affects. A quick scan for files containing
<defs>tells you the scope of the cleanup and which icons are worth measuring. - Step 2Map your external reference surface first — Design systems theme icons via CSS and toggle effects via JS. Before purging, list ids referenced from outside the SVG (stylesheet
url(#…), scriptedgetElementById) — these are the false-positive risks, because the document-only scan can't see them. - Step 3Purge on Pro, one icon at a time — Run each icon through the Unused Defs Purger (Pro tier). It removes id-bearing
<defs>children not matched byurl(#id)orhref="#id", deletes any emptied<defs>, and reportsDefs removed. - Step 4Record before/after for the changelog — Capture the
Defs removedcount and byte delta per icon. Removing dead defs is a visually-invisible change — document it as a patch-level optimisation, not a breaking icon change. - Step 5Gate with visual regression — Run the icon visual-regression suite (Storybook + Chromatic, Percy) after purging. Any icon that changes pixels was relying on a reference the scan couldn't see (external CSS/JS or cross-file
<use>) — exclude it from the purge or inline the reference. - Step 6Make it a contribution check — Document that new icons pass through the purger before review, and add it as a CI step so dead defs never re-accumulate between releases. Pair with a second pass for transitive orphans on complex icons.
Sources of dead defs in a design-system icon library
Where the sediment comes from, and whether this purger resolves it. Symptoms map directly to the reference test the tool applies.
| Source | Typical dead def | Resolved by purger? |
|---|---|---|
| Variant gradients exported with a single frame | Unreferenced linearGradient | Yes — no fill="url(#id)" matches it |
| Flattened mask / boolean edit | Orphaned clipPath / mask | Yes — no clip-path/mask reference |
| Removed effect left its filter behind | Unreferenced filter | Yes — no filter="url(#id)" |
| Icon dropped from a shared sprite | <symbol> with no in-file <use> | Risky — may be used cross-file (false positive) |
| Theme applies gradient via CSS | Gradient referenced only from a stylesheet | No — removed as false positive; keep out of purge |
Editor metadata, <title>/<desc> | Not a defs reference issue | No — use the Metadata Scrubber |
Design-system cleanup toolchain
Each tool does one job; the purger handles <defs> only. Sequence them for a complete icon-optimisation pass.
| Stage | Tool | Removes |
|---|---|---|
| 1. Dead definitions | Unused Defs Purger | Unreferenced gradients, clip-paths, filters, masks, markers, symbols |
| 2. Editor fingerprints | SVG Metadata Scrubber | <metadata>, <title>/<desc>, Illustrator/Figma/Inkscape attrs |
| 3. Markup minification | SVG Pro-Minifier | Comments, whitespace, redundant namespaces |
| 4. Coordinate precision | SVG Precision Tuner | Excess decimal places on path numbers |
Cookbook
Design-system workflows for auditing and purging defs at scale, with the cross-file risks an icon system actually faces.
Before/after on a themed brand icon
The icon shipped with light and dark gradient variants; only the default is referenced. Purge keeps one, drops one.
Before (defs): grad-light, grad-dark Reference scan: url(#grad-light) → 1 match; url(#grad-dark) → 0 After: Defs removed: 1 (grad-dark) Byte delta: -312 B on this icon. → Multiply across 800 icons for the library-level win.
Sprite symbol that must NOT be purged
icon-archive lives in a shared sprite and is used by <use href="#icon-archive"> on pages outside this file. Purging the sprite would delete it. Keep the master sprite out of the purge set.
sprite.svg contains <symbol id="icon-archive">…</symbol>
No <use href="#icon-archive"> inside sprite.svg itself.
If purged: Defs removed includes icon-archive → BROKEN refs
on every consuming page.
Rule: purge per-icon source files, never the assembled sprite.Theme gradient referenced from CSS — exclude it
The design system applies fill: url(#g-accent) from a stylesheet. The SVG itself never references g-accent, so the purger would remove it. Detect via the visual diff and exclude the file.
icon.svg defs: g-accent (no in-file url(#g-accent))
theme.css: .icon--accent { fill: url(#g-accent); }
Purge result: g-accent removed → themed icons render flat.
Visual diff: FAIL → add icon.svg to the purge exclude list
or inline the gradient reference into the SVG.Two-pass purge on a complex illustrated icon
A hero illustration has a filter that references a gradient; the filter is unused. Pass one removes the filter, pass two removes the now-orphaned gradient.
Pass 1: Defs removed: 1 (unused filter 'shadow')
gradient 'shadow-grad' KEPT (was referenced inside
the filter that just got removed)
Pass 2: Defs removed: 1 (shadow-grad now truly orphaned)
Library rule: loop until Defs removed == 0 for hero assets.CI contribution gate for new icons
Block a PR if a contributed icon still has unreferenced defs after the canonical purge — keeps the library clean by construction.
CI step (per changed icon):
1. run svg-unused-defs-purger → record Defs removed
2. run visual regression vs Storybook baseline
3. if Defs removed > 0 on the COMMITTED file → fail
("purge before committing")
4. if pixel diff > 0 → fail ("a ref was external")Edge cases and what actually happens
Purging the assembled shared sprite
Breaks refs (error)A sprite's <symbol>s are referenced by <use> on other pages, not inside the sprite file. The document-only scan sees no in-file reference and removes them, breaking every consumer. Purge per-icon source files; never run the assembled sprite through the purger. Build sprites with the SVG Sprite Builder.
Icon themed via external CSS
Removed in errorDesign systems often apply fill: url(#grad) from a stylesheet so a single icon can render in multiple themes. The purger can't see the stylesheet and removes the gradient. Gate on visual regression and exclude such icons, or inline the reference into each SVG.
Effect toggled by JavaScript at runtime
Removed in errorIf JS sets filter: url(#hover-glow) on interaction, the static markup never references hover-glow, so the purger strips it. Keep JS-driven icons out of the purge set or re-add the def post-purge — there is no preserve list to protect it.
Contributor expects the purger to also minify
Out of scopeThe purger only removes unreferenced <defs> children and empty <defs>. It does not strip comments, whitespace, or metadata. Sequence the Pro-Minifier and Metadata Scrubber after it for a full pass.
Library has duplicate ids across merged icons
Watch closelyWhen icons are concatenated (e.g. into one document), id collisions become invalid SVG and browsers resolve to the first match. The purger keeps any def whose id appears in a reference, so collisions may both survive. Namespace ids per icon before merging — the Sprite Builder does this when assembling sprites.
Transitive dead defs in illustrated icons
By designComplex icons often have filters that reference gradients. If the filter is unused, pass one removes it but keeps the gradient (the scan saw the reference before removal). Loop the purger until Defs removed is 0 for fully minimal output.
Free-plan contributor can't run the tool
Pro requiredThe purger is Pro-tier; free-plan contributors see an upgrade overlay. Run the purge in CI on a Pro/runner context rather than relying on every contributor to have the tool available locally.
Id-less defs survive even when dead
PreservedOnly id-bearing <defs> children are removal candidates. A definition with no id is always kept. If your exporter emits id-less dead defs, the purger won't drop them — add ids or strip them with another step.
Frequently asked questions
How often should I run the purger on an active icon library?
On every icon-changing PR, enforced as a CI check. Because the tool is deterministic and option-free, it produces the same result every time, which makes it safe to run continuously and keeps dead defs from re-accumulating between releases.
Does cleaning defs affect icon versioning?
Removing unreferenced defs is visually invisible, so it is a patch-level change, not a breaking one — assuming the visual-regression gate passes. Document it as an optimisation in the changelog.
Can I safely purge our shared sprite file?
No — the sprite's <symbol>s are referenced by <use> on other pages, which the document-only scan can't see, so they'd be removed. Purge the per-icon source files before they're assembled, never the finished sprite.
What about icons themed with external CSS?
Those are the main false-positive risk. A gradient referenced only from a stylesheet looks dead to the purger and gets removed. Either inline the reference into each SVG or exclude themed icons from the purge — and always gate on visual regression.
Will it remove our editor metadata too?
No. The purger only handles <defs> references. For <metadata>, <title>/<desc>, and Illustrator/Figma/Inkscape attributes, run the SVG Metadata Scrubber as a separate stage.
Can I batch-purge the whole library in one operation?
The tool processes one SVG per run, so library-scale cleanup is a loop over files (in CI or via the desktop runner), not a single multi-file call. The result is identical per file because there are no options.
How do I prove the optimisation to stakeholders?
Aggregate the per-file Defs removed counts and byte deltas. Even a few hundred bytes per icon adds up meaningfully across a large library, and the metric is concrete enough for a design-system doc or engineering blog.
What tools do large design systems use for SVG optimisation?
Most combine SVGO in the build with lint rules for consistency. SVGO's default config doesn't aggressively remove unreferenced defs with this tool's exact scan, so the Unused Defs Purger fills that specific gap — just validate its output against your themed/sprited icons because of the external-reference blind spots.
Do I need two passes for illustrated icons?
Often yes. A filter referencing a gradient keeps the gradient alive on pass one even if the filter is itself unused. Loop the purger until Defs removed reaches 0 for complex assets.
Is anything uploaded when contributors run it?
No. The web tool runs in the browser (0 bytes uploaded badge), and the desktop runner runs locally. Icon source never leaves the machine.
Why did an icon look broken after purging?
A def it relied on was referenced from outside the SVG — external CSS, JavaScript, or a cross-file <use> — so the document-only scan removed it. Restore from source, exclude that icon from the purge, and inline the reference if you want it to be purge-safe.
Where does the purger sit in the build order?
First. Remove dead <defs> before scrubbing metadata, minifying, and tuning precision, so each later stage operates on the smallest, cleanest markup.
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.