You export an icon from Figma, drop it into the page, and inspect the file. It is 4.2 KB. The same glyph, hand-authored, would be 380 bytes. The vector is identical — same shape, same viewBox, same pixels on screen — but the exported version carries eleven decimal places per coordinate, three namespaces it will never use, a comment with the designer’s name, and a hidden layer that was turned off before export. SVG optimization is the work of removing that overhead without touching the geometry. Done well, a 4 KB icon becomes a 400 byte icon and nothing on the page looks different.
Why SVGs bloat
An SVG is XML text. Every byte of metadata, every namespace declaration, every comment, every trailing zero is a byte the browser downloads and parses before it ever draws a line. Design tools are generous with this overhead because their priority is round-tripping the file back into the editor, not serving it over the wire.
A typical Illustrator or Inkscape export includes:
- An XML declaration (
<?xml version="1.0" encoding="UTF-8"?>) the browser does not need inline. - Editor namespaces (
xmlns:inkscape,xmlns:sodipodi,xmlns:x) that exist only so the editing app can re-read its own metadata. - A
<metadata>block containing RDF, a license URL, and the creator’s name. - Comments, including auto-generated ones.
- Hidden layers and paths with
display="none"that ship bytes but render nothing. - Coordinate values like
12.345678901234567where12.346would render identically.
None of this affects the picture. All of it costs bytes.
Strip: metadata, namespaces, comments, hidden layers
The first pass is pure deletion. Open the file and remove anything that exists for the editor’s benefit, not the browser’s.
Before:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24" height="24" viewBox="0 0 24 24">
<metadata>
<rdf:RDF><cc:Work><dc:format>image/svg+xml</dc:format>
<dc:creator><cc:Agent><dc:title>Jane</dc:title></cc:Agent></dc:creator>
</cc:Work></rdf:RDF>
</metadata>
<sodipodi:namedview id="base" showgrid="false" />
<path d="M3 12 L9 18 L21 6" stroke="black" fill="none" />
<path d="M0 0 L1 1" style="display:none" />
</svg>
After:
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M3 12 9 18 21 6" stroke="black" fill="none"/>
</svg>
Strip the <?xml ?> declaration (it can trigger quirks in some HTML parsers when inlined). Strip the DOCTYPE if one was carried over from an old SVG 1.1 file. Strip <metadata>, every inkscape: and sodipodi: attribute, the <sodipodi:namedview>, and any element with display="none". If a layer is invisible, the bytes describing it are pure waste — delete the element and its children.
Keep the default xmlns="http://www.w3.org/2000/svg" declaration. Lose it and the file stops being an SVG.
Reduce: precision, groups, transforms
After deletion, attack redundancy.
Decimal precision. Path data is the biggest line item in most icons, and coordinate values routinely carry 8 to 15 decimal places. A 24×24 icon rendered at 48 pixels has a half-pixel grid; anything past three decimals is noise. Round to 3 (sometimes 2) significant figures.
Before: d="M3.00000001 11.98765432 L8.98765432 18.01234567 L21.01234566 6.01234567"
After: d="M3 12 9 18 21 6"
Some optimizers go further and rewrite coordinates as relative offsets when shorter, drop the space between a command and its negative number (L9-2 instead of L9 -2), and remove the leading zero (0.5 → .5).
Redundant groups. A <g> with a single child and no attributes serves no purpose — unwrap it. A <g> that applies a transform or stroke to one shape can often be inlined by pushing that attribute onto the child.
Merged paths. Two adjacent paths with identical stroke and fill can be combined into one d attribute. Less XML structure, one set of paint attributes instead of two.
Transforms. transform="translate(2,3)" on a path can sometimes be baked into the coordinates themselves for a net byte saving — though if the math makes the numbers longer, leave it. This is a judgement call; the optimizer will usually pick correctly.
Minify and let the server compress
Once the file is structurally clean, collapse whitespace and shorten IDs. id="path847" becomes id="a" when only one element references it; IDs that are never referenced get removed entirely. Shorten stroke-linecap="round" to nothing if it matches the default.
At this point the file is as small as plain text will get. The next win happens at transport. SVG compresses extremely well because it is repetitive XML — a 900-byte icon typically travels as 280 bytes over the wire once Brotli does its work. Make sure your server is sending Content-Encoding: br (or gzip as fallback) and the right Content-Type: image/svg+xml. Most CDNs and static hosts do this by default; verify with the Network tab.
The HTTP layer is where the biggest remaining gains live. Do not chase byte savings in the source file once you are below transport-encoding compression — you are optimizing against Brotli, and Brotli is very hard to beat by hand.
Optimize, then compare
Run the file through the SVG tool to apply the strip-and-reduce pass automatically, then eyeball the rendered output against the original at the size you actually ship. If your icon is meant to sit at 20px in a button, check it at 20px — a curve that looks wrong at 2000% zoom in your editor can look fine at real size, and vice versa.
For the broader question of whether SVG is even the right format, raster formats still win for photographic content and complex illustrations with thousands of paths. A 60 KB PNG will beat a 240 KB SVG every time for a hero image. Compare the raster alternative with the image compressor before committing to vectors for anything dense.
The checklist
Strip: XML declaration, DOCTYPE, <metadata>, editor namespaces, comments, hidden layers, unreferenced IDs. Reduce: path precision to 3 decimals, redundant single-child groups, duplicate paint attributes. Minify: collapse whitespace, shorten referenced IDs. Compress: hand the file to Brotli or gzip at the server and stop worrying about the source bytes. Vector art only where vectors earn their keep — measure against a rasterized alternative when the path count climbs.
FAQ
Does SVG optimization affect visual quality?
It should not. Rounding coordinates to 3 decimals moves vertices by less than a thousandth of a user unit, which is invisible at any realistic display size. If you see artifacts, raise precision back to 4 or check whether the optimizer collapsed a path you needed to keep separate.
Which is better, SVGO or manual editing?
SVGO (and tools built on it) handles 90% of the work deterministically — namespace stripping, precision rounding, ID shortening. Manual editing earns its keep on decisions SVGO will not make for you, like whether a hidden layer should actually be deleted or whether two paths should stay separate for animation.
Should I inline SVG or use an <img> tag?
Inline when you need CSS styling, theming, or animation on the SVG’s internals — that is the main reason to choose SVG over a raster in the first place. Use <img src="*.svg"> or a CSS background when the file is static and reusable; the browser caches it and your HTML stays small.