SVG Optimizer
Run SVGO with every toggle explained, measured before/after bytes and a real diff — entirely on this device.
Only an <svg> root is accepted. Every byte figure below is measured, not estimated.
Default runs SVGO's preset-default set; No preset runs only the plugins you tick below. Decimal places kept in path coordinates. 3 is SVGO's own default; 0 moves geometry. Repeats the whole pass until the output stops shrinking. Indents the result. Bigger file, and the line diff below becomes readable.
What ran preset-default · precision 3 · multipass on · pretty off
SVG is XML: a missing quote or an unclosed tag is refused, not repaired.
Before
After
Both pictures are a neutralised copy: scripts, event handlers and external references are removed before rendering. The download is computed from your original bytes.
Size report
- Original
- —
- Optimized
- —
- Gzip
- —
- Saved
- —
Diff — original against optimized
The line diff appears here after a run.
Batch — optimize several files
Add up to 20 .svg files; each one is measured on its own.
No zip archive: Download all saves each file separately, so your browser may ask permission for multiple downloads. Batch files are never put in a link or stored between visits.
Press ? for shortcuts
Do it offline:$ npx svgo input.svg -o output.svg
How to use it
- Paste your SVG or drop one or more .svg files — batch mode gives each file its own row and savings figure.
- Leave the default preset alone for a safe pass, or open the plugin list and switch individual members off.
- Set path precision — 3 decimals is svgo's default, 1 is aggressive, 0 moves your geometry.
- Read the report card and the diff, compare the two previews, then copy or download the result.
Drop an icon export in and read what SVGO actually did to it: which of 22 plugins ran, how path coordinates were rewritten, and the before and after byte counts with a line diff you can scroll. Every toggle names the risk it carries, so removing a title or a viewBox is a decision you make rather than a surprise you ship. Nothing is uploaded — svgo runs in a worker in this tab.
How it works
Four steps run in a fixed order, and the order is the honesty. First the source is tokenized as XML and the root element is checked, because svgo itself does not verify that it was handed an SVG: feed the optimizer an HTML fragment and it happily returns <html/>, a 99% saving on a file that draws nothing. Second the plugin config is assembled — pure data, printable before any optimizer is loaded. Third svgo runs. Only then are bytes counted, once per side, so the status line, the report card and the downloaded file can never disagree.
Most of the win is in path data. convertPathData re-parses every d attribute, converts absolute commands to relative where that is shorter, merges runs of the same command and rounds coordinates to the precision you set: M12 3.00000v11.500000 comes back as M12 3v11.5 without moving a pixel, because trailing zeros carry no geometry. Rounding does move pixels. At 3 decimals a 20-unit drawing shifts by at most half a thousandth of a unit; at 0 every control point snaps to a whole unit and curves visibly change shape, which is why that setting warns.
The plugin list is two mechanisms in one surface. Fifteen of the 22 are members of svgo's default preset and are switched off through overrides — the preset still runs, minus that member. The other seven are standalone entries appended after the preset, in a fixed documented order, and the resolved array is shown to you. Multipass then repeats the whole sequence until a pass stops shrinking the file, which is what lets collapseGroups and convertShapeToPath feed each other: a rect becomes a path, the wrapping group becomes unnecessary, the next pass removes it.
Examples
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<!-- exported from Figma -->
<title>Download</title>
<desc>An arrow pointing into a tray</desc>
<g id="icon-download" fill="none" stroke="#111827" stroke-width="2">
<path d="M12 3.00000v11.500000"/>
<path d="M7.28345 10.11235L12 14.85000L16.71655 10.11235"/>
<rect x="3" y="17" width="18" height="4" rx="1"/>
</g>
</svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><title>Download</title><desc>An arrow pointing into a tray</desc><g fill="none" stroke="#111827" stroke-width="2"><path d="M12 3v11.5m-4.717-4.388L12 14.85l4.717-4.738"/><rect width="18" height="4" x="3" y="17" rx="1"/></g></svg>The default preset on an icon export: 426 B → 312 B, saved 114 bytes (27%). Indentation and the editor comment go, the unused group id goes, the two arrow paths merge into one d attribute with a relative m command, and the accessible title and description are still there.
<!-- traced in Inkscape -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="#2563eb" d="M2.51234 10.25873C2.5 5.75123 5.75873 2.50412 10.25236 2.5S17.5 5.75 17.5 10.25 14.25 17.5 10.25 17.5 2.5 14.75 2.51234 10.25873z"/></svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="#2563eb" d="M2.5 10.3c0-4.5 3.3-7.8 7.8-7.8s7.2 3.3 7.2 7.8-3.2 7.2-7.2 7.2-7.8-2.7-7.8-7.2"/></svg>The same traced blob at 1 decimal: 250 B → 172 B, saved 31%. The default 3 decimals lands at 201 B instead, so the extra two decimals cost 29 bytes here — cheap insurance on a 20-unit canvas, and worth comparing in the previews before you keep it.
<svg viewBox="0 0 32 32" width="32" height="32" xmlns="http://www.w3.org/2000/svg">
<!-- brand mark, do not edit -->
<title>Logo</title>
<desc>Two overlapping squares</desc>
<rect x="2" y="2" width="20" height="20" fill="#0f766e"/>
<rect x="10" y="10" width="20" height="20" fill="#14b8a6" fill-opacity="0.80000"/>
</svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><!--brand mark, do not edit--><title>Logo</title><desc>Two overlapping squares</desc><path fill="#0f766e" d="M2 2h20v20H2z"/><path fill="#14b8a6" fill-opacity=".8" d="M10 10h20v20H10z"/></svg>removeDimensions on, comment and description removal off: 331 B → 252 B, saved 24%. width and height are gone while viewBox stays, so the mark now scales to whatever box the page gives it; both rects became paths and 0.80000 became .8.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" x="0px" y="0px" viewBox="0 0 32 32" width="32px" height="32px" xml:space="preserve">
<metadata>Made with Illustrator</metadata>
<circle fill="#FF0000" cx="16" cy="16" r="15"/>
</svg><svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="32" height="32" viewBox="0 0 32 32"><circle cx="16" cy="16" r="15" fill="red"/></svg>An untouched Illustrator export is where the big numbers live: 374 B → 153 B, saved 221 bytes (59%). The XML declaration, the DOCTYPE, the metadata block, version and the zeroed x and y all go; 32px becomes 32 and #FF0000 becomes the shorter keyword red.
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><defs><linearGradient id="brandGradient" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#f43f5e"/><stop offset="1" stop-color="#6366f1"/></linearGradient></defs><rect x="0" y="0" width="24" height="24" rx="4" fill="url(#brandGradient)"/><polygon points="7,12 11,16 17,8" fill="none" stroke="#fff" stroke-width="2"/></svg><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><defs><linearGradient id="a" x1="0" x2="1" y1="0" y2="1"><stop offset="0" stop-color="#f43f5e"/><stop offset="1" stop-color="#6366f1"/></linearGradient></defs><rect width="24" height="24" fill="url(#a)" rx="4"/><path fill="none" stroke="#fff" stroke-width="2" d="m7 12 4 4 6-8z"/></svg>414 B → 369 B, saved 11%. brandGradient is renamed to a and the url(#…) reference follows it; the polygon becomes a path; the zeroed x and y disappear. Switch cleanupIds off and the same file lands at 393 B — 24 bytes is the price of keeping the name your CSS or your <use> element points at.
<svg fill="red" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z"/></svg><svg xmlns="http://www.w3.org/2000/svg" fill="red" viewBox="0 0 24 24"><path d="M0 0h24v24H0z"/></svg>A file that has already been through this pass: 102 B in, 102 B out, saved 0 bytes. sortAttrs reorders the two attributes and nothing else changes, so the page says so plainly instead of inventing a gain.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Sloppy markup is refused, not repaired
SVG is XML, and XML has no error recovery: an unclosed tag, a duplicate attribute or a bare & stops the run with the line and column that broke it, input untouched. A second gate catches what the first accepts — &foo; is entity-shaped, so tokenizing passes and svgo's own parser rejects it, and you get that message verbatim rather than a half-optimized file.
removeViewBox and removeDimensions fight, and the viewBox wins
Turning both on does not strip both. The pair is resolved in one documented order and the measured result is a file that keeps viewBox="0 0 W H" and loses width and height — the responsive shape, which is almost certainly what you wanted. If you truly need a fixed-size file, leave removeDimensions off; a file with neither cannot be scaled at all.
Minified ids break outside references
cleanupIds shortens ids that are only used inside the file, and rewrites the url(#…), href and clip-path references that point at them. What it cannot see is a stylesheet, a script, an aria-labelledby attribute or a <use> element in another document reaching for #brandGradient. If anything outside the file names an id, switch that plugin off.
<title> and <desc> are the accessible name and description
A <title> child of the root is what a screen reader announces for an image role, and <desc> is its long form. removeTitle and removeDesc are the two toggles this page marks as an accessibility risk, and each warns when the element really left the output. Note what the default preset actually does: removeDesc only deletes editor boilerplate such as "Created with Sketch", so a description you wrote survives a default run and no warning is raised — the output pane is the proof.
No xmlns, no standalone rendering
An <svg> without xmlns="http://www.w3.org/2000/svg" renders inline in HTML and nowhere else: not as an <img> source, not as a CSS url(), not opened in a browser tab. That is why removeXMLNS is grouped with the risky plugins, and why a missing declaration in your input is warned about instead of quietly carried into the output.
Scripts and event handlers stay unless you say otherwise
<script> elements and onclick-style attributes are markup like any other, so the default preset keeps them and the structure readout names them with positions. removeScripts deletes them. The two previews never execute anything: they draw a neutralised copy through an image element, and each construct removed to make that copy safe is listed beside it.
The previews are not the download
What you see is the sanitised twin of each side; what you copy or save is computed from your original bytes. Rasterized equivalence is also not something this page can prove — the tests pin bytes, structure and the plugin set, and comparing the two previews at the size you will actually ship is still your job.
Embedded raster data barely moves
An embedded bitmap payload is already compressed bytes with no whitespace, no coordinates and nothing to round, so a file that is mostly one pasted screenshot reports a saving near zero. removeRasterImages deletes such elements outright, with one measured caveat: in svgo 4.0.2 it matches the legacy xlink:href attribute only, so an <image href="photo.png"> is left alone.
Batch mode, one file at a time, no archive
Up to 20 files run as separate optimizations with their own rows and their own savings; a file that fails its gate shows its own message and does not stop the rest. Download all issues one save per file rather than building a zip, so your browser may ask about multiple downloads.
Common errors
This tool optimizes up to 4 MB of SVG; beyond that, use the CLI: npx svgo input.svg -o output.svg- Past four megabytes the paste is declined whole rather than optimized in part, and your text stays in the editor. A file that big is usually a traced photograph; the printed command runs the same svgo release from disk.
The root element is <html>, not <svg> — this tool needs SVG markup.- Copying from a page inspector brings the wrapper along. This is the gate that exists because svgo would have answered <html/> and called it a saving — copy the <svg> element itself, or open the .svg file.
Invalid character '<' in name at line 1, column 11 — probable cause: element names may contain letters, digits, '-', '.', '_' and ':' only.- The XML gate speaking: <rect was never closed, so the next < lands inside a tag name. Jump to error puts the caret on column 11, and the code frame shows the line around it.
Invalid character entity at line 1, column 70 — probable cause: the optimizer's XML parser refused this markup; SVG is XML, so it is not repaired for you.- svgo's parser, mapped into the same positioned shape as every other error here. &foo; looks like an entity but no SVG document defines it; write &foo; if you meant the literal text.
The input <svg> has no xmlns attribute, so the optimized file has none either — it will not render on its own or as a data URI, only inline in HTML.- A warning, not a refusal: the file is still optimized and still usable inline. Add the namespace declaration before saving it as a standalone asset.
"removeTitle" is on and this file has a <title> element: removing it drops the accessible name screen readers announce.- The a11y chip, raised only when the element is actually present. Decorative artwork marked aria-hidden loses nothing; anything carrying meaning should keep its title.
FAQ
How much smaller does an SVG really get?
It depends entirely on what your editor left behind. The Illustrator export above drops 59% with no toggles touched, because most of it was a DOCTYPE, metadata and indentation. A hand-written icon gives up 27%, and a file already through this pass gives up nothing. The report card prints the measured numbers for your file rather than an average.
Which toggles can change how my drawing looks?
Nine of the 22 carry that risk and are marked: cleanupIds, inlineStyles, removeHiddenElems, convertPathData, reusePaths, removeViewBox, removeDimensions, removeXMLNS and removeRasterImages. The four the default preset runs are named together in one chip on every default pass; a toggle you tick names itself, and path precision 0 raises one of its own. The rest only remove bytes a renderer never reads.
Why did my gradient id become a single letter?
cleanupIds shortens every id it believes is file-local and rewrites the references it can find. It is one of the larger wins on gradient-heavy artwork and one of the easier ways to break a sprite sheet, so it has its own switch and the diff shows every rename.
Can I drop width and height but keep the viewBox?
Yes — turn on removeDimensions and leave removeViewBox off. Turning both on gives the same result, because the pair is resolved in an order that preserves the coordinate system; the measured output keeps viewBox and loses the two size attributes either way.
Why refuse broken markup instead of fixing it?
Because guessing at a repair changes a drawing without telling you. HTML parsers are required to recover from tag soup; XML parsers are required not to, and SVG is XML. You get the line, the column and a code frame, your input untouched.
Is anything sent to a server?
No. The optimizer is a JavaScript library loaded into a worker in this tab on your first run, dropped files are read locally, and there is no endpoint to post to. Sharing a link is an explicit action that encodes the state into the URL fragment, which browsers never transmit.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Run the current buffer again with the toggles as they stand.
- ⌘/Ctrl + S
- Save the optimized markup as an .svg file, ahead of the browser's save-page action.
- ⌘/Ctrl + C
- Copies the whole result when nothing is selected; a selection copies as usual.
- ⌘/Ctrl + Shift + C
- Copy the whole result regardless of the selection.
- t
- Switch theme, unless a text field or an IME composition has focus.
- ? or ⌘/Ctrl + /
- Open the help dialog listing these bindings.
- Esc
- Dismiss the top dialog. It never empties the editor.