This post was drafted with AI assistance and reviewed by the PerfectVector team.
SVGO made our badge almost 40% smaller and broke the page it lived on. The file still rendered fine on its own. What disappeared was an id the surrounding HTML used to recolor one shape, and nothing in SVGO's output warns you about that. If your page, script or sprite reaches into an SVG, run the optimizer on a copy and test the copy where it's actually used.
Here's the fixture: a 437-byte hand-made badge whose sun is <circle id="badge-accent" ... fill="#ef8067"/>, inlined in a page with #badge-accent { fill: #f5bf42; } outside the SVG. With SVGO 4.1.0 in a clean folder (so no stray svgo.config.mjs is picked up), npx svgo input.svg -o default.svg writes 263 bytes, and the id is gone. The sun stays coral instead of turning gold. That's documented behaviour: cleanupIds "removes unused IDs", and an ID referenced only by CSS in another document looks unused from inside the file. (Its back-off for <style> and <script> only applies when those elements are inside the SVG itself.)
If you can name the hooks, keep the rest of the preset and protect just those IDs:
// svgo.config.mjs export default { plugins: [ { name: 'preset-default', params: { overrides: { cleanupIds: { preserve: ['badge-accent'] }, }, }, }, ], }; npx svgo input.svg --config svgo.config.mjs -o preserved.svg wc -c input.svg default.svg preserved.svg We re-ran it on 2026-11-04 and read the computed fill of the inlined circle in headless Chrome:
| File | Bytes | #badge-accent present | Computed fill |
|---|---|---|---|
| Original | 437 | yes | rgb(245, 191, 66) gold |
| Default preset | 263 | no | rgb(239, 128, 103) coral |
preserve: ['badge-accent'] | 281 | yes | rgb(245, 191, 66) gold |
Keeping the hook cost 18 bytes. Turning cleanupIds off entirely (cleanupIds: false) gave the same 281 bytes here, but preserve (or preservePrefixes for a naming scheme like icon-) still cleans IDs you didn't ask for, which helps once several SVGs are inlined on one page. These numbers are for this one fixture. Your byte counts will differ.
An ID is only one kind of dependency, though. After any optimizer run (SVGO directly, or one your bundler runs for you later), check what consumes the file: CSS selectors, JS queries, gradient, clip and mask references, sprite <symbol> IDs, and the viewBox your layout scales with. Then test every state that touches it, like hover, theme and animation, because a static preview won't exercise them. And keep the unoptimized master: SVGO is for delivery copies, not the file someone will edit next.
The full guide includes the copy-paste fixture, a dependency checklist table, notes on numeric precision, viewBox and SVGZ, and an FAQ: SVGO: Optimize an SVG Without Losing Needed Structure
We build PerfectVector, an image-to-vector tool.

