How to Optimise SVG for File Size and Render Speed
"Optimising an SVG" describes two jobs that have almost nothing to do with each other. One is making the file smaller, which is about bytes crossing a network. The other is making it cheaper to draw, which is about how many elements the browser has to lay out, style and paint. Minifying a file does very little for the second, and a page that stutters is usually suffering from the second.
1. Two problems wearing one name
- File size is what you fix with minification: editor metadata, long decimals, redundant attributes. It affects how fast the file arrives, and it is mostly a solved, automatable problem.
- Render cost is what you fix with fewer elements. It affects every frame after the file arrives — style recalculation, layout and paint — and no minifier can help, because a shorter attribute is still a node.
A 4 KB file with nine thousand paths in it is small and slow. A 400 KB file with twelve paths is large and fast. Work out which one you have before choosing a tool, because the two problems have no fixes in common.
2. How the file reaches the page decides most of this
Before touching the markup, decide how it is delivered. This choice sets the caching behaviour, whether the nodes land in your DOM at all, and whether you can style the thing — and it constrains everything you do afterwards.
<!-- 1. Inline. Styleable and scriptable, no extra request
— but re-sent with every page, never cached on its
own, and every node lands in your DOM. -->
<svg viewBox="0 0 24 24"><path d="..." /></svg>
<!-- 2. <img>. Cached as its own file, off your DOM,
isolated from your CSS — so you cannot recolour it. -->
<img src="/icons/star.svg" alt="" width="24" height="24">
<!-- 3. CSS background. Same isolation, no markup at all.
Invisible to assistive technology: decoration only. -->
<div class="star"></div>
<!-- 4. Sprite and <use>. Define the shape once,
reference it anywhere. -->
<svg class="icon"><use href="#star" /></svg>- Inline costs no request and can be recoloured by CSS or driven by script, but it is re-sent inside the HTML on every page load and it puts every path into your document.
- <img> is cached like any other asset and stays out of your DOM entirely, at the price of a request and total isolation from your stylesheet.
- CSS background behaves like
<img>with no markup at all, but it is invisible to assistive technology — decoration only, never content. - Sprite plus
<use>defines a shape once and references it everywhere, which is the right answer for a repeated icon.
The common mistake is inlining a large illustration on every page of a site. Nothing in it is cacheable, so the same twenty kilobytes are re-downloaded on every navigation — a cost no amount of minification touches. A file that never changes and is never styled belongs in an <img>.
3. What actually makes a file big
In rough order of how much they cost, and how easily they go:
- Embedded raster data. An
<image>holding a base64 PNG is the reason behind almost every genuinely enormous SVG. It is a bitmap wearing a vector's file extension, and no vector optimiser will shrink it. Find it first. - Coordinate precision. Usually the largest honest win, and the subject of the next section.
- Editor metadata. Inkscape and Illustrator write their own namespaces, layer names and document settings into the file. A renderer never reads any of it.
- Redundant attributes.
fill-opacity="1"is the default; writing it down changes nothing. - Empty and nesting-only groups. Editors leave a lot of
<g>elements that hold one child and no attributes.
4. Precision, and where rounding stops being free
An editor computing a curve keeps far more decimals than a screen can resolve. 256.0000000001 and 256 paint the same pixel, and path data is where most of a file's bytes live, so cutting decimals is the single biggest lossless saving available.
The part almost every guide leaves out is that this stops being lossless at exactly the point where the number is not a coordinate. Round an opacity to zero decimals and 0.8 becomes 1 — the element is now solid. Round a stroke width the same way and a hairline doubles in weight. Those are edits to the drawing, not compressions of it, and they need a floor.
<!-- Four decimals: what an editor exports. -->
d="M256.0000 100.2457 L356.1235 300.9123 H155.8765 Z"
<!-- One decimal: identical on screen at any size a
browser will actually paint. -->
d="M256 100.2L356.1 300.9H155.9Z"
<!-- Where blunt rounding stops compressing and starts
editing the drawing: -->
opacity="0.8" -> opacity="1" <!-- now solid -->
stroke-width="0.5" -> stroke-width="1" <!-- twice as heavy --><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<!-- Created by Adobe Illustrator -->
<g id="body">
<path class="st0" d="M256.0000000001,100.2456723945 L356.1234506782,300.9123456782 L155.8765493218,300.9123456782 Z" fill="#FF7A00" />
</g>
</svg><svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"> <path d="M256 100.2L356.1 300.9H155.9Z" fill="#FF7A00" /> </svg>
That pair is measured, not estimated: 450 bytes down to 129, a 71% saving, from nothing more than deleting what Illustrator wrote for itself and rounding coordinates to one decimal. Your own files will differ — a drawing that is mostly path data saves more, and one that is mostly gradients and filters saves less.
5. Compression is a server setting, not a file setting
SVG is text, and text compresses. Gzip or Brotli will typically do more for the transferred size of an SVG than minification does — and unlike minification it costs nothing in fidelity. The catch is that image/svg+xml is missing from plenty of default compressible-types lists, so the saving silently does not happen.
# Compression is a server setting, not a file setting. # SVG is text and gzips well, but only if the server has # been told that image/svg+xml is compressible. Plenty of # default configurations have not. # nginx gzip_types image/svg+xml; # Apache AddOutputFilterByType DEFLATE image/svg+xml
Check the response headers before optimising anything by hand. A file served without content-encoding has a much larger and much easier win available than a precision pass. The older .svgz approach — shipping a pre-gzipped file — still works, but it needs its own server rules and breaks anything that reads the file directly, so on-the-fly compression is the simpler choice.
6. Node count is the render problem
Every element in an inline SVG is a real node in your document. It is matched against your selectors, it takes part in style recalculation, and it occupies memory. This is why Lighthouse ships an "avoid an excessive DOM size" audit at all — though its thresholds have moved between versions, so read the number your current version reports rather than one from an article.
Auto-traced artwork is the usual culprit. A drawing converted from a photograph can carry thousands of near-identical paths, each a few bytes but each a full participant in layout. Simplifying the artwork — fewer shapes, merged overlapping paths, a lower trace detail setting — is the only fix, and it happens in a vector editor, not in a minifier.
7. Repeated icons: define once, reference many times
If the same icon appears forty times, forty copies of its path are forty copies of the same bytes. A <symbol> defined once and pulled in with <use> removes that duplication from the source.
<!-- Defined once, near the top of the document. -->
<svg hidden xmlns="http://www.w3.org/2000/svg">
<symbol id="star" viewBox="0 0 24 24">
<path d="M12 2l3 7h7l-6 4 2 7-6-4-6 4 2-7-6-4h7z" />
</symbol>
</svg>
<!-- Referenced as often as you like. Each reference is
one node in your source instead of a full copy of
the path. The browser still expands it into a shadow
tree, so this shrinks the markup more than it
shrinks the render cost. -->
<svg class="icon" aria-hidden="true"><use href="#star" /></svg>
<svg class="icon" aria-hidden="true"><use href="#star" /></svg>Worth being honest about the limit: <use> shrinks the markup considerably, but the browser still expands each reference into a shadow tree, so the rendering engine has a similar amount of work to do either way. It is a file-size and maintainability technique that helps the DOM somewhat — not a way to make a thousand icons free.
8. Do not optimise what is not slow
A dozen icons on a page are not a performance problem, and shaving three hundred bytes off each one will not produce a measurable change in anything. The costs described above show up at scale — a map with thousands of regions, a chart redrawing every frame, a traced illustration with a five-figure path count.
Measure before you start. If the file is large, look at what is inside it. If the page is janky, count the nodes and record a performance profile. Optimising the wrong axis is how people spend an afternoon getting a 6 KB file down to 5 KB while the actual stall goes untouched.
Where to go next
Our optimiser does the structural half of this list in your browser — the file is never uploaded. It strips comments, editor namespaces and authoring metadata, drops attributes that only restate a default, removes ids that nothing references, unwraps groups that merely nest, and rounds numbers to a precision you choose. It keeps <title> at every setting, because that is the accessible name rather than metadata, and it holds opacity and stroke width above a minimum precision for the reason in section 4. It reports the real before and after byte counts, so you can see what a given setting actually bought.
What it does not do, so you know where the boundary is: it will not compress the file for transfer, which is your server's job; it will not touch embedded raster data; and it will not merge paths, convert shapes or simplify geometry — those change the drawing, and this pass is meant to leave the rendered result identical. For fewer nodes rather than fewer bytes, the editor is where the artwork itself gets simplified.
