SVG Formatter and Optimizer

Paste an export and read the markup you actually shipped. The middle panel draws the file after every clean-up pass, so a stroke going missing shows up next to the line that removed it rather than three commits later.

SVG formatting bench

Output
Source.svg
Render
ResultWaiting for markup
0Bytes in
0Bytes out
0%Trimmed
0Elements kept
0Bytes as data URI
  • Renders while it cleans
  • Respects inherited attributes
  • Runs in your tab

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 findWritten byEffect on the render
sodipodi:namedview, inkscape:zoomInkscapeNone. It records where the canvas was scrolled.
<metadata> with an RDF blockInkscape, ScribusNone. Licence and author fields nothing reads.
data-name, duplicated id valuesIllustrator, FigmaNone, unless your CSS targets them.
<g> wrappers with no childrenAny layer-based editorNone. A layer someone emptied and never deleted.
width and height on the rootEvery editorReal. They set an intrinsic size that CSS then has to fight.
Coordinates like 48.1174022Every editorReal 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.

Straight from the editor
<path d="M 30.4829134,48.1174022
L 43.2094718,60.8439605
L 66.1143987,35.9268412"/>
Two decimals, minified
<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.

PrecisionSuitsWatch out for
0 decimalsIcons drawn on a whole-pixel grid at 16, 24, or 32 unitsAnything with a curve. Control points snap and the arc flattens.
1 decimalUI icons and simple logosHairline strokes under 0.5 units wide.
2 decimalsMost artwork, and the default hereNothing at icon scale.
3 to 4 decimalsMaps, charts, and any viewBox wider than a few thousand unitsLittle. The saving shrinks as the safety grows.
Leave numbers aloneGenerated output you plan to diff against a source of truthFile 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.

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

  1. A gradient goes flat. Something removed a <defs> block whose id a fill="url(#name)" pointed at. Every ID referenced by url() or an href is collected here before the first pass runs, and the notes panel counts them. Check that count matches what you expect.
  2. 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.
  3. An animation stops. SMIL begin attributes reference other elements by ID, as in begin="start.click". Those IDs are read as references too, so the elements survive.
  4. Text reflows. Whitespace inside <text> is content. Formatters that indent text children insert spaces a reader sees. Nothing inside text elements is touched here.
  5. The file stops parsing elsewhere. The XML declaration is not re-emitted, since a document served as image/svg+xml or 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

  1. 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.
  2. Paste it above with every clean-up box on and precision at two decimals. Read the notes panel before you read the byte counters.
  3. 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.
  4. If the icon is going into a component, turn on Drop root width and height and confirm the render still fills its box.
  5. Beautify and commit for a file that lives in your repository. Minify and copy for markup going inline or into a stylesheet.
  6. 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

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.

Questions about formatting SVG

Precision, inherited attributes, data URIs, and the parts a cleaner should not touch.

Will formatting change how my SVG looks?

Beautifying alone changes nothing, because whitespace between elements has no meaning in SVG. The clean-up options do change the file, and each one reports what it did. Coordinate rounding moves points by a measured amount the notes panel prints in user units. Dropping root width and height changes how the graphic sizes itself, which is why that box starts off. The render beside the code updates after every pass, so a change you did not want shows up straight away rather than after you commit.

What precision should I use for icons?

Two decimals for almost every icon, which is the default. Precision is relative to the viewBox rather than to the pixels on screen, so on a 24 unit icon two decimals already describes a hundredth of a unit, far below what any display resolves. Drop to one decimal for flat icons with straight edges. Go to zero only for artwork drawn on a whole-unit grid, since curve control points snap at that setting and arcs visibly flatten. Maps and charts with a viewBox in the thousands should stay at three or four.

Why did an attribute set to its default value survive the clean-up?

Because a parent element sets the same property to something else, so the attribute is doing real work. Presentation attributes inherit down the tree. A group carrying stroke-width of 2 passes it to every child, and a child written as stroke-width of 1 is overriding that parent rather than repeating the spec default. Removing it would double the stroke on that shape. Every ancestor is checked before a default is dropped, and the notes panel names the attributes it kept and why.

Should I remove width and height from the root element?

Remove them when the graphic is going into a component or a stylesheet sizes it, and keep them when the file is opened on its own or referenced by an img tag that needs an intrinsic size. Without them the viewBox alone sets the aspect ratio and the CSS box sets the scale, which is what you want for an icon. The option does nothing when the root has no viewBox, since a file with neither has no way to scale and would crop instead.

Is the minified output safe to paste inline into HTML?

Yes, with one thing to check. Inline SVG shares the document ID space, so two icons on the same page both carrying an ID such as gradient will collide, and the second one silently borrows the first one gradient. Rename IDs per icon, or use a sprite sheet with a symbol element instead of pasting several files into one page. Everything else transfers cleanly, since the minified form is valid markup with the whitespace removed.

Why is the data URI not base64 encoded?

Because base64 makes an SVG about a third larger and impossible to read. SVG is text, so percent encoding the handful of characters CSS objects to gives a shorter string that still shows the markup when you open the stylesheet. The encoder here swaps double quotes for single quotes and escapes the angle brackets, hash, percent, ampersand, and braces. Every current browser accepts that form in a url() value.

Can this replace a build-step optimizer?

For an icon you are pasting into a component, yes. For a build pipeline, no, and it is not trying to. A dedicated optimizer converts curves between command types, merges consecutive path segments, flattens transforms into coordinates, and rewrites shapes as paths. Those passes save more and each carries a way to go wrong, which is a fair trade inside a build you can test but a poor one in a browser tab where you paste and go.

My file failed to parse. What does that mean?

SVG is XML, so the rules are stricter than HTML. Every tag needs closing, attribute values need quotes, an ampersand in text has to be written as an entity, and tag names are case sensitive, which makes viewbox a different attribute from viewBox. The parser message in the output pane names the line and column. A common cause is markup copied out of an HTML page where the browser had already repaired something the source got wrong.

Does the preview run scripts inside my SVG?

No. Script elements and any on-event attributes are stripped from the copy sent to the preview panel before it renders. Your output pane keeps the original markup untouched, so a file with animation logic downloads exactly as you pasted it. The preview is a picture of the geometry, not a sandbox for running the file.

Is my artwork uploaded anywhere?

No. Parsing, clean-up, formatting, and the preview all run in JavaScript inside this page. No request is made after the page loads, and nothing is stored between visits. Closing the tab clears both panes, so unreleased branding or a client asset under embargo stays on your machine.