Reading an SVG export before you ship it
An icon leaves a design tool as one unbroken line of markup carrying layer names, a zoom level from the last editing session, and coordinates written to seven decimal places. None of that draws a pixel. Formatting it is the first step in seeing what the file actually contains, and the second step is deciding which parts of it deserve to survive into a repository. The bench above does both at once, which matters more here than in any other formatter, because SVG is code you have to look at to trust.
What an export leaves behind
The junk is predictable, and it varies by which application wrote the file. Turning on Drop editor metadata clears the first four rows below in one pass.
| What you find | Written by | Effect on the render |
|---|---|---|
sodipodi:namedview, inkscape:zoom | Inkscape | None. It records where the canvas was scrolled. |
<metadata> with an RDF block | Inkscape, Scribus | None. Licence and author fields nothing reads. |
data-name, duplicated id values | Illustrator, Figma | None, unless your CSS targets them. |
<g> wrappers with no children | Any layer-based editor | None. A layer someone emptied and never deleted. |
width and height on the root | Every editor | Real. They set an intrinsic size that CSS then has to fight. |
Coordinates like 48.1174022 | Every editor | Real but tiny. Roughly a millionth of a pixel at icon scale. |
Load the sample and watch the byte counters. The file starts at a little over a kilobyte and lands near a third of that, and the render in the middle panel does not flinch. That gap is the whole argument for cleaning exports before they go anywhere near a build.
Precision is where the bytes hide
A path with forty points and seven decimals per coordinate spends more than half its length on digits below the threshold of vision. Rounding is the single change with the best ratio of bytes saved to risk taken, and the notes panel reports the cost in the only unit that matters.
<path d="M 30.4829134,48.1174022
L 43.2094718,60.8439605
L 66.1143987,35.9268412"/><path d="M30.48 48.12L43.21 60.84
L66.11 35.93"/>Seventy-one characters became thirty-six, and the furthest any single point moved is under five thousandths of a user unit. On a 96 unit icon rendered at 24 pixels, that is a shift of about one eight-thousandth of a pixel. Read the note the tool prints after each run, because the same setting behaves differently on a map or a floor plan.
| Precision | Suits | Watch out for |
|---|---|---|
| 0 decimals | Icons drawn on a whole-pixel grid at 16, 24, or 32 units | Anything with a curve. Control points snap and the arc flattens. |
| 1 decimal | UI icons and simple logos | Hairline strokes under 0.5 units wide. |
| 2 decimals | Most artwork, and the default here | Nothing at icon scale. |
| 3 to 4 decimals | Maps, charts, and any viewBox wider than a few thousand units | Little. The saving shrinks as the safety grows. |
| Leave numbers alone | Generated output you plan to diff against a source of truth | File size. Nothing is trimmed from path data. |
Precision scales with the viewBox, not with the pixel size on screen. A viewBox of 0 0 24 24 gives each unit real weight, so two decimals is already fine detail. A viewBox of 0 0 4000 3000 makes one unit a quarter of a pixel on a typical render, and rounding to zero decimals there is invisible while saving far more.
Beautify to review, minify to serve
The two output modes answer different questions, and picking the wrong one wastes the pass.
- Beautify when the file is going into version control. One element per line means a code review shows the shape someone edited instead of a single changed line ten thousand characters wide. Turn on One attribute per line for a file with heavy gradients or filters, where a single element carries a dozen attributes and a diff should point at the one that moved.
- Minify when the markup is going inline into a template, a CSS background, or a JavaScript string. Whitespace between elements has no meaning in SVG outside of text content, so removing it costs nothing. Minify also drops the leading zero from decimals, turning
0.28into.28, which is legal in path data and in every presentation attribute.
Text is the exception in both modes. Content inside <text>, <tspan>, <title>, and <style> is left byte for byte, since a space between two words there is a space a reader sees.
Why a default attribute sometimes stays
Removing attributes already set to their default value is standard practice. fill-opacity="1" and stroke-miterlimit="4" say what the spec already says. The rule breaks the moment inheritance enters, and this is the bug most cleaners ship.
Presentation attributes inherit down the tree. A <g> carrying stroke-width="2" passes that to every child. A circle inside it written as stroke-width="1" is asking for the spec default, and it is doing real work, because it overrides the parent. Delete it as redundant and the circle jumps to a 2 unit stroke. This tool checks every ancestor before removing a default, keeps the attribute when a parent sets the same property, and says so in the notes. The sample file contains exactly this case on purpose.
The same caution applies to <style>. A file with an internal stylesheet may select on IDs or class names, so nothing here removes either when a style element is present. A note tells you when that guard fired.
viewBox, width, and height
The viewBox defines the coordinate system. width and height define an intrinsic size. Keeping all three is right for a standalone file opened in a browser and wrong for an icon dropped into a component, where the CSS should decide the size and the two attributes fight it.
<!-- from the editor, locked to 96 by 96 --><svg width="96" height="96" viewBox="0 0 96 96"><!-- after dropping the pair, sized by CSS --><svg viewBox="0 0 96 96">.icon { width: 1.25rem; height: 1.25rem; }Drop root width and height stays off by default, since it is the one setting with a visible consequence you might not want. Turning it on when the root has no viewBox does nothing, and the notes panel says why. An SVG with neither a viewBox nor dimensions has no way to scale, and a browser will crop it rather than resize it.
Getting SVG into a CSS background
An inline data URI keeps a small icon out of the network waterfall. The trap is encoding. Base64 inflates the markup by roughly a third and makes the result unreadable, while percent encoding the few characters CSS objects to keeps the file legible and smaller.
.field-valid {background-image: url("data:image/svg+xml,%3Csvg xmlns=
'http://www.w3.org/2000/svg' viewBox='0 0 96 96'%3E …");
background-repeat: no-repeat;}Copy data URI builds that string. Double quotes inside the markup become single quotes, then <, >, #, %, &, and the braces are percent encoded, and an xmlns is added if the fragment lost it. The Bytes as data URI tile shows the encoded length, which is what your stylesheet pays. Watch it while you change the precision setting, since hash-heavy colour values and long path strings both inflate once encoded.
Two practical limits. Data URIs load without a request but never cache separately, so an icon used on forty pages ships forty times unless the stylesheet itself is cached. And a data URI in CSS cannot inherit currentColor, so an icon that changes colour with its button belongs inline in the markup instead.
Five ways a cleaned SVG breaks
- A gradient goes flat. Something removed a
<defs>block whoseidafill="url(#name)"pointed at. Every ID referenced byurl()or anhrefis collected here before the first pass runs, and the notes panel counts them. Check that count matches what you expect. - Strokes thicken or vanish. An inherited presentation attribute was dropped as a default. The ancestor check above prevents it, and the warning note names the attributes involved.
- An animation stops. SMIL
beginattributes reference other elements by ID, as inbegin="start.click". Those IDs are read as references too, so the elements survive. - Text reflows. Whitespace inside
<text>is content. Formatters that indent text children insert spaces a reader sees. Nothing inside text elements is touched here. - The file stops parsing elsewhere. The XML declaration is not re-emitted, since a document served as
image/svg+xmlor embedded inline has no use for it. Add it back by hand for a file consumed by a stricter XML parser.
A pass from export to production
- Export at the artboard size you designed on. A 24 unit icon exported at 24 units rounds better than the same icon exported at 1024 and scaled down later.
- Paste it above with every clean-up box on and precision at two decimals. Read the notes panel before you read the byte counters.
- Switch the render background between grid, light, and dark. A white stroke on a white background is the classic export bug, and it hides on a light preview.
- If the icon is going into a component, turn on Drop root width and height and confirm the render still fills its box.
- Beautify and commit for a file that lives in your repository. Minify and copy for markup going inline or into a stylesheet.
- Serve it with gzip or brotli. Compression works on repetition, and formatted SVG compresses close to its minified form over the wire, so the beautified file in your repo costs very little in transfer.
That last point resolves an argument people have often. Minifying is for markup pasted into another file, not for a static asset behind a compressing server, where readability in the repository is worth more than the bytes.
What this formatter leaves alone
- Path geometry is preserved. Numbers are rounded, but no curve is converted, no absolute command becomes relative, and no consecutive lines are merged. Those rewrites save more and are where a dedicated optimizer earns its keep.
- Groups are never unwrapped. A
<g>with a single child and no attributes could be flattened. It is left in place, because a transform or a class name on that group may be the hook your JavaScript uses. - Unused defs stay. A gradient nothing references is kept. Detecting a reference from inside a CSS rule or from script is beyond what a static read of the file supports, and deleting a live gradient is a worse failure than a few unused bytes.
- Colours are not shortened.
#f4f1e9keeps its form and named colours stay named. The saving is a handful of bytes and the risk of a wrong conversion is not worth it. - Attribute order is source order. Sorting attributes would make diffs against your editor exports noisier for no rendering gain.
- Large files slow the tab. Parsing and serialization run on the main thread. Icons and illustrations are instant. A traced map with tens of thousands of path nodes will make the render lag while you type.
Nothing you paste leaves your machine. The parser, the clean-up passes, and the preview all run inside this page, so a client logo under embargo stays local. Load the page once, drop your connection, and the bench keeps working.
