How to svg unused defs purger: edge cases, limitations, and faq
- Step 1List ids referenced outside the SVG — Before purging, find ids used from external stylesheets and scripts — anything like
fill: url(#…)in a.cssfile orgetElementById('…')in JS. These are the defs the document-only scan will wrongly remove. There is no preserve box, so the protection is to keep those files out of the purge. - Step 2Confirm your defs actually have ids — Only id-bearing
defs > *children are evaluated. If a def you expect to be removed survives, check it has anidand is a direct child of<defs>(not nested inside another element). - Step 3Run the purge on Pro — The tool is Pro-tier; free users see an upgrade overlay. Drop a single SVG (or paste source), click Process SVG, and read
Defs removed. - Step 4Diff the preview against the original — The result panel renders the purged SVG. Compare it visually to the source. If anything vanished, a reference lived somewhere the scan can't reach — restore and exclude that file.
- Step 5Re-run for transitive orphans — If you have nested def-to-def references, run the output through the purger again. A second pass removes defs that only became dead after the first removed their sole referrer.
- Step 6Verify size and format before large jobs — Keep files under the 50 MB Pro ceiling and ensure pasted markup is valid SVG XML (root
<svg>, no parser error). Both are checked before processing and will abort the run if violated.
Reference detection: what the scan sees
The purger matches exactly two patterns, document-wide. Anything outside the file is invisible.
| Reference location | Detected? | Consequence |
|---|---|---|
fill/stroke/clip-path/mask/filter/marker-* = "url(#id)" in the SVG | Yes | Def kept |
href="#id" / xlink:href="#id" (<use>, gradient chaining) in the SVG | Yes | Def kept |
url(#id) written inside another <defs> child | Yes | Def kept (indirect reference) |
fill: url(#id) in an external CSS file | No | Def removed (false positive) |
getElementById('id') / scripted url(#id) in JS | No | Def removed (false positive) |
<use href="#id"> on a different HTML page | No | Symbol removed (cross-document blind spot) |
Paint without url(), e.g. fill="#id" | No | Def removed (malformed markup) |
Limits and gates
The constraints that abort or shape a run, with the real numbers from the implementation.
| Constraint | Value / behaviour |
|---|---|
| Tier required | Pro (free users get an upgrade overlay, no run) |
| Per-file size limit | 50 MB on Pro (5 MB is the free SVG ceiling on non-Pro tools) |
| Files per run | One (single-file tool; not multi-file) |
| Options | None — no preserve list, no settings panel |
| Input validation | Pasted source must parse as SVG XML (root <svg>, no parser error) |
| Cascade depth | Single pass; nested def-to-def orphans need a re-run |
| Upload | Zero — browser/runner local (0 bytes uploaded badge) |
Cookbook
Troubleshooting recipes for the cases where automatic detection and true usage diverge.
False positive: gradient referenced from external CSS
The SVG never references g1 internally; a stylesheet does. The purger removes g1. The fix is to inline the reference or exclude the file.
icon.svg defs: <linearGradient id="g1">…</linearGradient>
icon.svg body: <path d="…"/> (no url(#g1) here)
style.css: path { fill: url(#g1); }
Purge: Defs removed: 1 (g1) → icon renders with no fill.
Fix: inline <path fill="url(#g1)" .../> then purge is safe.Id-less dead def that survives
A clipPath with no id is dead but never a removal candidate, so Defs removed stays 0 for it.
<defs> <clipPath><rect/></clipPath> <!-- no id --> </defs> Purge: Defs removed: 0 (id-less defs aren't evaluated). To drop it: add an id, then it becomes a normal candidate.
Single-pass cascade leaves an orphan
maskA is unused; inside it is url(#gB). Pass one removes maskA but keeps gB. Pass two removes gB.
<defs> <mask id="maskA"><rect fill="url(#gB)"/></mask> <linearGradient id="gB">…</linearGradient> </defs> <!-- nothing references #maskA --> Pass 1: Defs removed: 1 (maskA); gB kept. Pass 2: Defs removed: 1 (gB).
Animated reference via SMIL is detected
A SMIL animate sets filter to url(#fx). Because that url(#fx) is literal text in the SVG, the scan sees it and keeps fx.
<defs><filter id="fx">…</filter></defs> <rect> <animate attributeName="filter" to="url(#fx)" .../> </rect> Scan: url(#fx) present in markup → fx KEPT. (CSS @keyframes referencing #fx would NOT be seen — external.)
Invalid pasted markup is rejected before processing
A truncated SVG fails XML validation, so nothing is purged and you get an Invalid SVG error.
Pasted: <svg><defs><linearGradient id="g"> <!-- truncated -->
Result: "Invalid SVG — could not parse the pasted content"
→ no run. Paste complete markup or upload the file.Edge cases and what actually happens
Def referenced only from external CSS
Removed in errorThe purger reads only the SVG document. A fill: url(#id) in a separate stylesheet is invisible, so the def is removed. There is no preserve box to rescue it — inline the reference into the SVG, or keep externally-styled files out of the purge.
Def referenced only from JavaScript
Removed in errorIds resolved by getElementById or applied as url(#id) dynamically don't appear as static references in the markup, so they're removed. Exclude JS-driven SVGs from the purge or re-add the def afterwards.
SMIL animation referencing a def
SupportedA SMIL <animate to="url(#id)"> puts the url(#id) literally in the markup, so the scan detects it and keeps the def. This works because the reference is in-document text — unlike CSS @keyframes in an external stylesheet, which is not seen.
Nested SVG documents
Watch closelyAn inner <svg> is parsed as part of the outer document, and the scan runs over the whole serialized tree. Inner-defs ids are checked against the document-wide reference pool, which is usually what you want — but verify, since an inner def referenced only across an outer-document boundary you didn't expect could be affected.
Id-less def that is genuinely dead
PreservedOnly id-bearing defs > * children are removal candidates. A dead def with no id is always kept. Add an id if you want the purger to be able to remove it.
Single-pass transitive orphan
By designA def referenced only inside another def that is itself being removed survives pass one (the scan saw the reference before deletion). Run the purger again to collect it; loop until Defs removed is 0.
Reference missing the url() wrapper
Reference failPaint references must use url(#id). A malformed fill="#id" is not matched, so the def is judged dead. Well-formed SVG is unaffected; this only bites hand-edited markup that dropped url().
Free-tier user
Pro requiredThe tool is Pro-tier. Free users see an upgrade overlay instead of the upload area — there is no free run and no reduced-capability mode. Upgrade to Pro (50 MB per-file limit) to use it.
File over the size limit
413 rejectedFiles above the 50 MB Pro ceiling are blocked before processing with a tier-limit message. For SVG text that is huge — usually a sign of embedded base64 raster, which the purger doesn't process anyway.
Invalid pasted SVG
Invalid inputPasted source must parse as XML with a root <svg> and no parser error. Truncated or malformed markup is rejected with an "Invalid SVG" message and nothing runs. Paste complete markup or upload the file.
Frequently asked questions
Can the purger detect ids referenced in external CSS?
No. It analyses only the SVG document. An id referenced from a separate stylesheet (fill: url(#id)) is invisible and the def will be removed. There is no preserve list — inline the reference into the SVG or keep externally-styled files out of the purge.
How are animated defs handled?
SMIL animations that set url(#id) write that reference into the markup, so the scan detects it and keeps the def. CSS @keyframes in an external stylesheet that reference an SVG id are not seen — same external-CSS caveat applies.
Is there really no preserve-id field?
Correct — there are no options at all. Older descriptions that mention a preserve list or settings panel are inaccurate. The only control you have is which files you run through the tool.
Why did a def I thought was unused survive?
Three common reasons: its id appears in some url(#id)/href="#id" you overlooked (including inside another def); it has no id (id-less defs are never removed); or it's only dead because of a chain that needs a second pass to resolve.
Why did a def I needed get removed?
It was referenced from outside the document — external CSS, JavaScript, or a <use> on another page — which the document-only scan can't see. Restore it, exclude that file from the purge, and inline the reference if you want the file to be purge-safe.
What happens with nested SVG documents?
An inner <svg> is parsed as part of the outer document and its defs are checked against the document-wide reference pool. That's usually the desired behaviour, but verify nested artwork after purging.
What is the maximum file size?
50 MB per file on Pro. SVG is text, so that's an enormous vector file — hitting the limit usually means embedded base64 raster data, which the purger does not touch.
Does it change anything besides <defs>?
No. It removes unreferenced id-bearing <defs> children and any <defs> left empty. Markup, metadata, coordinates, and artwork are otherwise untouched. Use the Pro-Minifier, Metadata Scrubber, and Precision Tuner for those.
Do I ever need to run it twice?
Yes, for nested def-to-def references. A def kept alive only by a def that's being removed survives the first pass. Re-run until Defs removed is 0 for a fully minimal <defs>.
Does it remove a def referenced with a bare #id (no url)?
Paint references via a bare fill="#id" (without url()) are not matched, so the def is treated as unused. Valid SVG always uses url(#id) for paint, so well-formed files are safe; only malformed hand-edits are at risk.
Is my SVG uploaded for analysis?
No. Processing is local — in the browser tab or the desktop runner — and the result panel shows a 0 bytes uploaded badge. The id-reference analysis runs on your device.
What is the output named?
<original-stem>-purged.svg. The metric Defs removed: N and the before/after byte sizes appear in the result panel.
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.