DEV Community

楊東霖
楊東霖

Posted on • Originally published at devplaybook.cc

SVG Optimization Best Practices for Web (2025)

SVG is the native format for scalable web graphics — icons, logos, charts, illustrations — and it's often the most overlooked performance opportunity on a page. A fresh Figma export can be 10x larger than it needs to be.

This guide covers how to optimize SVGs systematically: automated tools, manual techniques, delivery strategies, and common mistakes to avoid.


Why SVG Optimization Matters

An unoptimized SVG from a design tool contains:

  • Editor metadata (<sodipodi:namedview>, <dc:format>, etc.)
  • Unused definitions and symbols
  • Redundant id attributes
  • Default attribute values that browsers assume anyway
  • Unnecessary whitespace and comments
  • Verbose path data (more decimal places than needed)
  • Empty groups and redundant transform attributes

A typical Figma icon export might be 4KB. After optimization: 800 bytes. Multiply that across 50 icons on a page.


Tool 1: SVGO (The Standard)

SVGO (SVG Optimizer) is the industry standard for SVG optimization. It's what most other tools use under the hood.

Install:

npm install -g svgo
Enter fullscreen mode Exit fullscreen mode

Basic usage:

# Optimize a single file (overwrites original)
svgo icon.svg

# Output to a new file
svgo icon.svg -o icon.min.svg

# Optimize all SVGs in a directory
svgo -f ./src/icons/ -o ./dist/icons/

# Show how much was saved
svgo icon.svg --pretty
Enter fullscreen mode Exit fullscreen mode

Configuration file (svgo.config.js):

module.exports = {
  plugins: [
    'preset-default',
    {
      name: 'removeViewBox',
      active: false,  // Keep viewBox for responsive scaling
    },
    {
      name: 'removeDimensions',
      active: true,  // Remove width/height (use CSS instead)
    },
    {
      name: 'prefixIds',
      params: {
        prefix: 'icon-',  // Prefix IDs to avoid conflicts when inlining
      },
    },
  ],
};
Enter fullscreen mode Exit fullscreen mode

Common flags to know:

# Multipass (multiple optimization rounds)
svgo --multipass icon.svg

# Disable a specific plugin
svgo --disable=removeViewBox icon.svg

# Pretty print (readable output)
svgo --pretty icon.svg
Enter fullscreen mode Exit fullscreen mode

Tool 2: Online SVG Optimizers

For one-off optimizations without installing anything:

  • SVGOMG (jakearchibald.github.io/svgomg): GUI interface for SVGO with toggle-able plugins and live preview
  • Nano SVG (vecta.io/nano): Alternative optimizer with its own algorithm

These are ideal for quickly checking what's safe to remove before committing to settings.


Manual Optimization Techniques

Some optimizations require human judgment that automated tools can't make.

1. Remove Unnecessary Metadata

Figma exports include metadata that's meaningless for web display:

<!-- Remove this -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:cc="http://creativecommons.org/ns#"
     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.0.dtd"
     xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
  <sodipodi:namedview />
  <rdf:RDF>...</rdf:RDF>

<!-- Keep this -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
Enter fullscreen mode Exit fullscreen mode

2. Simplify Paths

Overly precise paths have more decimal places than screens can render:

<!-- Before: 6 decimal places -->
<path d="M12.000000 4.000000 L20.000000 20.000000 L4.000000 20.000000 Z"/>

<!-- After: 1-2 decimal places is sufficient -->
<path d="M12 4 L20 20 L4 20Z"/>
Enter fullscreen mode Exit fullscreen mode

SVGO's roundPathData plugin handles this automatically, but you can set the precision:

{
  name: 'roundPathData',
  params: { precision: 2 }
}
Enter fullscreen mode Exit fullscreen mode

3. Merge Similar Elements

Design tools often export individual elements that could be combined:

<!-- Before: 3 separate paths with identical fill -->
<path fill="#333" d="M..."/>
<path fill="#333" d="M..."/>
<path fill="#333" d="M..."/>

<!-- After: one group with the shared attribute -->
<g fill="#333">
  <path d="M..."/>
  <path d="M..."/>
  <path d="M..."/>
</g>
Enter fullscreen mode Exit fullscreen mode

4. Replace Generic Shapes With SVG Primitives

Design tools sometimes export rectangles and circles as <path> elements. Replace them with semantic SVG:

<!-- Instead of a path that draws a circle -->
<path d="M 100 50 A 50 50 0 1 0 100 50.001 Z"/>

<!-- Use the circle primitive -->
<circle cx="100" cy="50" r="50"/>
Enter fullscreen mode Exit fullscreen mode

Primitives are smaller and more readable.

5. Remove Invisible Elements

Clean up clipping masks and invisible elements:

<!-- Invisible elements waste bytes -->
<rect width="24" height="24" fill="none"/>  <!-- Remove -->
<path opacity="0" d="..."/>                  <!-- Remove -->
Enter fullscreen mode Exit fullscreen mode

Inline SVG vs External SVG

Use Inline SVG When:

  • Icons that change color via CSS (:hover, dark mode)
  • SVGs that need JavaScript interaction
  • Critical above-the-fold icons (saves an HTTP request)
  • Animated SVGs
<!-- Inline: directly in HTML, styleable via CSS -->
<button class="btn">
  <svg viewBox="0 0 24 24" aria-hidden="true">
    <path d="M12 2L2 7l10 5 10-5-10-5z"/>
  </svg>
  Download
</button>
Enter fullscreen mode Exit fullscreen mode
/* Color via CSS parent */
.btn:hover svg path {
  fill: white;
}

/* Dark mode support */
@media (prefers-color-scheme: dark) {
  svg path { fill: #e2e8f0; }
}
Enter fullscreen mode Exit fullscreen mode

Use External SVG When:

  • Illustrations and complex graphics (cacheable)
  • SVGs used as <img> tags (no JS needed)
  • SVGs reused across many pages
<!-- External: cached by browser, simpler markup -->
<img src="/icons/logo.svg" alt="Company Logo" width="120" height="40">
Enter fullscreen mode Exit fullscreen mode

SVG Sprite for Icon Systems

For many icons, a sprite reduces HTTP requests:

<!-- sprite.svg (loaded once) -->
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
  <symbol id="icon-search" viewBox="0 0 24 24">
    <path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
  </symbol>
  <symbol id="icon-user" viewBox="0 0 24 24">
    <path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/>
    <circle cx="12" cy="7" r="4"/>
  </symbol>
</svg>

<!-- Usage anywhere in the page -->
<svg aria-hidden="true"><use href="#icon-search"/></svg>
<svg aria-hidden="true"><use href="#icon-user"/></svg>
Enter fullscreen mode Exit fullscreen mode

SVG Accessibility

Don't forget accessibility when embedding SVGs:

<!-- Decorative icons (no semantic value) -->
<svg aria-hidden="true" focusable="false">
  <path d="..."/>
</svg>

<!-- Meaningful icons without visible text -->
<svg role="img" aria-labelledby="icon-title">
  <title id="icon-title">Search</title>
  <path d="..."/>
</svg>

<!-- SVG with visible label -->
<button>
  <svg aria-hidden="true">
    <path d="..."/>
  </svg>
  <span>Search</span>
</button>
Enter fullscreen mode Exit fullscreen mode

Rule of thumb: if there's visible text describing the action, use aria-hidden="true" on the SVG. If the SVG is the only indicator of meaning, add a <title>.


SVG Animation Performance

Animations have their own performance considerations:

CSS Animations (Preferred)

/* High-performance: only animates transform and opacity */
@keyframes spin {
  to { transform: rotate(360deg); }
}

.loading-icon {
  animation: spin 1s linear infinite;
  /* Promote to GPU layer */
  will-change: transform;
}
Enter fullscreen mode Exit fullscreen mode

SMIL Animations (Avoid in Production)

SMIL (built-in SVG animations) has inconsistent browser support and poor performance. Prefer CSS or JavaScript (GSAP) animations instead.

Reduce Motion

Always respect the user's motion preference:

@media (prefers-reduced-motion: reduce) {
  .animated-icon {
    animation: none;
  }
}
Enter fullscreen mode Exit fullscreen mode

Build Pipeline Integration

Vite/webpack with vite-plugin-svgr

// vite.config.js
import svgr from 'vite-plugin-svgr';

export default {
  plugins: [svgr()],
};

// Usage in React
import { ReactComponent as SearchIcon } from './search.svg';

function SearchButton() {
  return (
    <button>
      <SearchIcon className="icon" aria-hidden="true" />
      Search
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

SVGO in package.json scripts

{
  "scripts": {
    "optimize-icons": "svgo -f src/icons/ -o public/icons/ --multipass",
    "prebuild": "npm run optimize-icons"
  }
}
Enter fullscreen mode Exit fullscreen mode

Quick Optimization Checklist

Before shipping an SVG:

  • [ ] Ran through SVGO with --multipass
  • [ ] viewBox preserved (for responsive scaling)
  • [ ] width/height removed (let CSS control size)
  • [ ] No editor metadata (Inkscape, Illustrator namespaces)
  • [ ] IDs prefixed if used inline (avoid collisions)
  • [ ] Accessibility attributes set (aria-hidden or aria-label)
  • [ ] Reduced motion considered for animated SVGs
  • [ ] Paths rounded to 1-2 decimal places

Related Tools


Download the Frontend Performance Checklist

The Frontend Performance Audit Checklist covers SVG optimization alongside image formats, font loading, bundle splitting, and Core Web Vitals — a complete audit you can run on any web project.


Level Up Your Dev Workflow

Found this useful? Explore DevPlaybook — cheat sheets, tool comparisons, and hands-on guides for modern developers.

🛒 Get the DevToolkit Starter Kit on Gumroad — 40+ browser-based dev tools, source code + deployment guide included.

Top comments (0)