Why your SVGs are 3× larger than they need to be.
Most SVGs exported from Figma, Illustrator, or Inkscape carry 50-80% bloat. Here's where the bloat comes from, which parts are safe to strip, and which parts will quietly break things if you're not paying attention.
Someone exports a cloud icon from Figma. The SVG is 4,142 bytes. They open the file in a text editor, expecting a few paths, and see a hundred-plus lines: Figma namespace declarations, a <metadata> block, two <defs> with unused clip paths, nested group elements, and coordinates like 12.33845720348597 with twelve decimal places of precision.
The same icon, run through a reasonable optimization pass: 472 bytes. Same rendering. Same visual output at every size. Eight times smaller.
This isn't unusual. Most SVGs from design tools carry 50-80% bloat, and most people never notice because the files are small to begin with. But on an icon-heavy site with dozens of SVGs inlined into the HTML, that bloat adds up to real kilobytes and real LCP regressions. Cleaning them up is one of the cheapest performance wins available to a front-end project.
A concrete before-and-after.
Here's the actual structure of a cloud icon from Figma, lightly abbreviated but truthful to the shape of what a real export looks like:
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24"
fill="none" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<metadata>Created with Figma</metadata>
<defs>
<clipPath id="clip0_12_345">
<rect width="24" height="24" fill="white"/>
</clipPath>
</defs>
<g clip-path="url(#clip0_12_345)">
<g id="Cloud Icon">
<path id="Cloud Shape"
d="M18.3847583920348 10.34283749824..."
stroke="#1F1F1F" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round"/>
</g>
</g>
</svg>
And here's the same icon after a pass through a reasonable optimizer:
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M18.38 10.34..." stroke="#1F1F1F" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Half the cruft is gone. The rendering is identical. The rest of this piece is about which specific reductions are safe, which aren't, and how to know the difference.
Where the bloat comes from.
Almost all of the excess comes from one of six sources.
1. Editor metadata
Figma adds <metadata>Created with Figma</metadata>. Inkscape adds <sodipodi:namedview> and <inkscape:*> elements. Illustrator adds its own dialect of metadata. These exist so the editor can round-trip the file without losing its own state — layer names, grid settings, color profiles. For editing purposes they're useful. For serving purposes they're dead weight.
2. Excessive coordinate precision
Design tools generate coordinates at double-precision floating-point — sometimes with fifteen decimal digits of precision. At a rendered size of 24 pixels, precision beyond about two decimal places is invisible. Every extra digit in every d attribute is bytes on the wire for resolution no human will ever perceive.
3. Unused <defs>
Figma loves to add a clip-path that bounds the icon to its artboard, then reference it via clip-path="url(#clip0_12_345)". For a square icon at its artboard bounds, the clip-path is a no-op — the icon was already drawn inside those bounds. Removing both the <defs> and the clip-path attribute produces the same rendering in exactly the cases where the artboard was tight around the art.
4. Nested empty-ish groups
Figma wraps the icon in a <g id="Cloud Icon">, which wraps the actual path. Inkscape wraps in multiple layer <g>s. If the groups apply transforms or attributes, they matter. If they're just organizational ("this is the cloud layer"), they don't. Empty groups can be collapsed.
5. Default-value attributes
SVG has a hundred-plus default values. A <rect> without a specified fill-rule defaults to nonzero. If your file specifies fill-rule="nonzero", it's adding bytes for no reason. Most common culprits: fill-opacity="1", stroke-opacity="1", vector-effect="none", preserveAspectRatio="xMidYMid meet" (the default).
6. XML declaration and comments
The <?xml version="1.0" encoding="UTF-8"?> line is required by the XML spec when the file is served as application/xml. For SVG inlined into HTML — the common case for icons — it's optional and removable. Comments from editors ("<!-- Generator: Adobe Illustrator 27.0 -->") serve no runtime purpose.
The five quick wins.
Five specific changes that account for ~80% of the total reduction. In order of impact on a typical icon:
| Change | Typical reduction | Risk |
|---|---|---|
Strip editor metadata (<metadata>, Sodipodi, Inkscape namespaces) | 10-30% | Very low. Only matters if you'll re-open in the editor. |
Remove unused <defs> and their references | Up to 50% on icon exports | Low, if you verify the clip/mask wasn't doing anything |
| Round coordinates to 2-3 decimal places | 10-30% on path-heavy SVGs | Low at typical rendering sizes; noticeable at huge sizes |
| Collapse empty groups that apply no attributes | 5-15% | Low |
| Drop default-value attributes | 5-10% | Very low |
Applied together on a typical Figma export, these reliably produce a 60-80% size reduction. The remaining bloat is either genuinely in-use structure or things you shouldn't touch.
Things to be careful about.
Three categories of thing that look like bloat but sometimes aren't.
IDs on elements. If you see id="Cloud Shape", the instinct is to strip it. But IDs are targets for CSS selectors (#cloud-shape { fill: blue; }) and JavaScript animations (document.getElementById('cloud-shape')). If the SVG is going to be interacted with at runtime, the IDs matter. If it's a static icon, they don't.
Namespace declarations. The xmlns at the root is required; most browsers will still render without it but some SVG-consuming tools will fail. xmlns:xlink is only required if you use xlink:href — which SVG2 deprecated in favor of plain href. Safe to drop if you're on a modern target; leave it if you need to support ancient tooling.
Title and desc elements. <title> and <desc> are the accessible equivalents of alt text for SVG. Screen readers read them. Stripping them strips accessibility. If the SVG is decorative (next to actual text that describes it), stripping is fine; if it's standalone content, leave them in.
What SVGO actually does.
SVGO is the de facto standard SVG optimizer — a Node.js tool with a plugin architecture, around since 2012, maintained actively. Its default configuration runs about thirty plugins in sequence, each doing one specific thing: remove XML declaration, remove editor namespaces, collapse groups, round coordinates, merge paths, and so on.
Most of SVGO's defaults are safe for static icons. A few aren't, and tripping them is how you end up with an SVG that still technically renders but is subtly broken — the stroke is a hairline off, an animation target ID got renamed, a gradient is clipped. The plugins worth specifically disabling for anything you might re-edit are cleanupIds (renames IDs to short ones like a, b — great for size, terrible if you use them in CSS) and prefixIds (prefixes IDs to avoid collisions — necessary for inline SVG sprites, surprising for standalone files).
The pattern for setting up SVGO on a project: run it with defaults, compare a handful of optimized SVGs to their originals visually, disable whichever plugin caused the problems you see. Ship.
What not to strip.
Three specific attributes that look strippable and aren't.
The viewBox. This is what makes an SVG responsive. If you strip it and keep only width and height, the SVG becomes fixed-size and will render weirdly at any other size. Never strip viewBox.
Explicit stroke widths on paths that are meant to stay hairlines. If you're going to scale the SVG, stroke-width scales with it — but at very small sizes, the default 1 can look too thick, and designers often specify stroke-width="0.5" for that reason. Rounding it to zero (which some over-eager optimizers do) breaks the rendering.
fill="currentColor". This is the pattern for making an icon inherit its color from the surrounding CSS. It looks like "bloat" compared to a specific color, but it's the whole point of how the icon will be used. Don't replace it with a hardcoded color "for efficiency."
How to verify nothing broke.
After optimizing a batch of SVGs, a quick sanity protocol. Takes about a minute per icon set.
- Visual diff. Render the original and optimized versions side by side at the sizes you actually use them. 24px, 48px, 96px. Look for differences. Usually there aren't any. When there are, they tell you which plugin was too aggressive.
- Check accessibility. If you stripped
<title>, add anaria-labelon the surrounding element. If you stripped<desc>, check whether the icon is standalone content or decorative. - Test animations and interactions. If the SVG has CSS animations tied to specific IDs, or JavaScript that targets specific elements, confirm they still work. This is the failure mode that silently ships to production.
- Confirm the viewBox. View the SVG at a size different from its declared
width/height. If it looks right, the viewBox is intact. If it looks cropped or stretched, something went wrong.
The checklist sounds like a lot. In practice, for a static icon set, you can run it in two minutes for a whole batch and then never think about it again. The alternative is shipping every Figma export at 4× the byte count they need to be, forever.
If you just want the tool.
The safe subset of these reductions — metadata, unused defs, coordinate rounding, default attributes, empty groups — can run entirely in a browser. No Node install, no SVGO setup, no config file.
SVG Slimmer.
Drop an SVG, get back a smaller version. Applies the safe subset of optimizations — strips editor metadata, rounds coordinates, removes unused defs, collapses empty groups — while preserving IDs, viewBox, and accessibility attributes. Runs in your browser. Your file doesn't upload.