Authoring, tooling and optimising SVG files

Clean up design-tool output, configure SVGO for a real project, keep the viewBox honest, and decide between a font, a sprite and inline markup.

What a design tool actually exports

<!-- Typical editor output. Everything here is a problem. -->
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generator: Some Tool 4.2.1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
     width="24px" height="24px" viewBox="0 0 24 24" version="1.1" id="Layer_1"
     xml:space="preserve" style="enable-background:new 0 0 24 24;">
  <style type="text/css">
    .st0{fill:#4A5568;}
    .st1{fill:none;stroke:#4A5568;stroke-width:2;}
  </style>
  <g id="Group_7" transform="translate(0.5,0.5)">
    <rect x="2" y="2" width="20" height="20" class="st0"/>
    <path class="st1" d="M6 12h12"/>
  </g>
</svg>

<!-- What you want to ship -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"
     fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
  <rect x="2" y="2" width="20" height="20"/>
  <path d="M6 12h12"/>
</svg>
Editor outputProblemFix
XML declarationMeaningless in an inline SVGRemove
Generator commentBytes and noiseRemove
xml:space, versionObsolete attributesRemove
width and height in pxBreaks fluid sizingKeep only viewBox, or make the sizes 100%
id on every groupCollides when inlined twiceRemove or prefix
Internal <style> classesLeaks into the page when inlinedConvert to presentation attributes
Nested translate groupsExtra nodes, harder to editBake into the coordinates
Number precision like 12.34567Bytes with no visual differenceRound to 1-2 decimals
⚠️
An internal <style> block in an SVG is not scoped. Inline that file into a page and .st0 can match your HTML, and your page CSS can restyle the icon. Convert the classes to attributes, or give every class a prefix unique to the icon.

SVGO with a deliberate configuration

npm install --save-dev svgo

# Inspect before you commit to a config
npx svgo --show-plugins
npx svgo --config svgo.config.mjs icon.svg -o icon.min.svg

# Batch a folder
npx svgo -f src/icons -o dist/icons --recursive
// svgo.config.mjs — a configuration for icon files
export default {
  multipass: true,                     // run the plugin chain until it settles
  js2svg: { indent: 0, pretty: false },

  plugins: [
    {
      name: 'preset-default',
      params: {
        overrides: {
          // Keep ids: they are referenced by gradients, clipPaths and masks.
          cleanupIds: false,
          // Do not collapse groups blindly: some carry transforms you rely on.
          collapseGroups: false,
          // Round to two decimals: visually identical, fewer bytes.
          cleanupNumericValues: { floatPrecision: 2 },
          // Keep the viewBox. Without it the icon cannot scale.
          removeViewBox: false,
          // Convert shapes to paths only when it is safe.
          convertShapeToPath: { convertArcs: true }
        }
      }
    },
    // Remove editor metadata
    'removeXMLProcInst',
    'removeComments',
    'removeMetadata',
    'removeEditorsNSData',
    'removeDimensions',                // drop width/height, keep viewBox
    {
      name: 'addAttributesToSVGElement',
      params: {
        attributes: [{ 'aria-hidden': 'true' }, { focusable: 'false' }]
      }
    }
  ]
};
  • removeViewBox must stay off. Removing it produces a file that cannot be scaled, which is the single most common SVGO mistake.
  • cleanupIds renames ids and can break references if a plugin is not aware of them. Keeping ids is safer for anything using gradients, masks or <use>.
  • multipass: true produces smaller output than a single pass because optimizing a path can enable a later optimization.
  • removeDimensions drops width and height so the icon fills its container — which is what you want for a sprite or a CSS-styled icon, and wrong for a fixed-size decorative image.
FileBeforeAfter SVGO
A single 24px icon1.2 KB0.4 KB
A detailed illustration82 KB34 KB
A traced bitmap logo240 KB180 KB
A chart exported from a tool45 KB21 KB
An icon with a gradient, ids kept1.8 KB0.9 KB

Font, sprite or inline

MethodColour controlCachingPer-icon costFits
Icon fontcolor onlyOne cached fileOne glyph, tens of bytesMany similar monochrome icons
Inline SVGFull: multi-colour, animationNot cached separatelyFull markup per useIcons that must be styled or animated
<img src>NoneCachedA request, or one from a spriteDecorative images
CSS backgroundNoneCachedOne rulePurely decorative marks
External sprite with <use>Limited: currentColor worksOne cached fileA few bytesA large icon set with some styling needs
SVG in a data URINoneCached with the CSS~33% base64 overheadVery small, fixed marks
<!-- The three shapes of the same decision -->

<!-- 1. Inline: the icon is styleable and animateable -->
<button class="btn">
  <svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false">
    <path d="M12 5v14M5 12h14" fill="none" stroke="currentColor" stroke-width="2"/>
  </svg>
  Add item
</button>

<!-- 2. Image: fixed, decorative, cacheable -->
<img src="/icons/logo.svg" alt="" width="120" height="32">

<!-- 3. Sprite reference: one cached file, many icons -->
<svg width="16" height="16" aria-hidden="true">
  <use href="/icons/sprite.svg#plus"></use>
</svg>

The practical default for most projects: inline the handful of icons whose colour or size changes with context, put everything else in a sprite or an <img>, and skip icon fonts unless the icon set is large, monochrome and static.

FAQ

Why does my icon disappear after optimising?
Almost always a removed viewBox or a broken id reference. Set removeViewBox: false and cleanupIds: false, then re-check the gradient, mask and <use> targets by name.
Should icons be inline or in a sprite?
Inline when the icon must change colour with state, be animated, or be accessible with its own title. A sprite when the set is large and most icons are used in a single colour — the bytes saved per use are substantial even if the sprite itself is larger than any single icon.

Symbol sprites, icon systems and reuse Shapes and coordinates

Last refreshed 2026-09-18.