DevToys Web Pro iconDevToys Web Proব্লগ
আমাদের রেট দিন:
ব্রাউজার এক্সটেনশন ব্যবহার করে দেখুন:
← Back to Blog

SVG Optimizer Guide: What SVGO Strips and What to Keep

10 min read

An SVG exported from Illustrator, Figma, or Inkscape carries a surprising amount of baggage: editor metadata, comments, redundant group nesting, default attribute values that the browser would apply anyway, and coordinate precision far beyond what any screen can render. SVGO (SVG Optimizer) is the standard tool for stripping that weight. Run your files through the SVG Optimizer to follow along with the examples below.

This guide explains exactly what SVGO removes, which plugins are safe to enable and which carry real risks, how path-data precision affects both file size and visual fidelity, and how the right optimization strategy changes depending on whether you serve SVG as an img tag, inline it in HTML, or use it as a CSS background.

What SVGO Actually Removes

SVGO is a plugin-based pipeline. Each plugin handles one category of cleanup. The default preset enables the plugins that are safe for almost every SVG. Understanding what each group removes helps you decide when to deviate from the defaults.

Editor metadata

Design tools embed proprietary namespaces and metadata that browsers ignore entirely. Illustrator adds xmlns:ai and xmlns:x namespaces with application version information. Inkscape adds sodipodi and inkscape namespace elements including grid settings, guides, and document history. Figma embeds a figma:metadata block with component IDs and last-edited timestamps. None of this data affects rendering, and it can add hundreds of bytes to every file.

Comments and processing instructions

The removeComments plugin strips all XML comments. This is safe for delivery assets — just be aware that if you use comments as human-readable section markers inside a file you edit by hand, they disappear after optimization. Processing instructions like <?xml version="1.0"?> are also removed because browsers do not require them for SVG.

Empty groups and hidden elements

Design tools often leave behind empty g elements from deleted layers, and elements with display:none or visibility:hidden that were used as construction guides. The removeEmptyContainers and removeHiddenElems plugins clean these up. The hidden-element removal has one edge case: if JavaScript later toggles the visibility of a hidden element, removing it from the markup will break that interaction. Disable removeHiddenElems for SVGs driven by JS.

Default attribute values

SVG has a large set of presentation attributes whose initial values match the SVG specification defaults — fill-opacity="1", stroke="none", display="inline", and many others. Design tools often write these out explicitly. The removeUselessDefs and removeDefaultPres plugins drop them because the browser applies the same values without them.

Excessive coordinate precision

Path data from vector editors routinely contains coordinates like 123.456789012. At a typical icon size of 24 × 24 px, the difference between 123.46 and 123.456789 is a fraction of a subpixel — invisible. The cleanupNumericValues and convertPathData plugins reduce precision to a configurable number of decimal places. This single plugin often accounts for 30–50% of the size reduction on complex illustrations.

Before and After

Here is a minimal real-world example showing what a Figma export looks like before and after SVGO processes it. The before version is not unusual — Figma is actually one of the cleaner exporters.

<!-- BEFORE: Figma export, 487 bytes -->
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <!-- icon: checkmark -->
  <g id="Frame" display="inline">
    <g id="check" fill-opacity="1" stroke="none">
      <path id="Vector" fill="#000000" fill-rule="evenodd" clip-rule="evenodd"
        d="M20.7071067811865 4.29289322 C21.0976310729820 4.68341751
           21.0976310729820 5.31658249 20.7071067811865 5.70710678
           L9.70710678 16.7071068 C9.31658249 17.0976311
           8.68341751 17.0976311 8.29289322 16.7071068
           L3.29289322 11.7071068 C2.90236893 11.3165825
           2.90236893 10.6834175 3.29289322 10.2928932
           C3.68341751 9.90236893 4.31658249 9.90236893
           4.70710678 10.2928932 L9 14.5857864
           L19.2928932 4.29289322 C19.6834175 3.90236893
           20.3165825 3.90236893 20.7071067811865 4.29289322Z"/>
    </g>
  </g>
</svg>

<!-- AFTER: SVGO default preset, 183 bytes -->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
  <path fill-rule="evenodd" clip-rule="evenodd"
    d="M20.707 4.293a1 1 0 0 1 0 1.414l-11 11a1 1 0 0 1-1.414 0l-5-5a1 1 0 1 1 1.414-1.414L9 14.586 19.293 4.293a1 1 0 0 1 1.414 0Z"/>
</svg>

That is a 62% reduction. The gains come from: removing the XML declaration and comment, collapsing the two redundant g wrappers, dropping fill="none" (the path overrides it), dropping fill-opacity="1" (default), dropping fill="#000000" (the path inherits currentColor or black by default), and rounding the path coordinates from 16 significant figures to 3.

Safe vs Risky Plugins

Not every SVGO plugin is safe to enable without thought. Three in particular cause breakage regularly enough to deserve explicit callouts.

PluginDefaultRiskRecommendation
removeViewBoxOffBreaks responsive scalingKeep OFF always
cleanupIdsOnBreaks JS/CSS/aria referencesOff for inline SVG
removeDimensionsOffSizing depends on container CSSOnly when CSS controls size
removeHiddenElemsOnBreaks JS-toggled visibilityOff for JS-animated SVG
convertColorsOnLoses named color intentSafe for icons, review for illustrations
mergePathsOnCan change visual stackingReview output visually

removeViewBox — keep it OFF

The viewBox attribute defines the internal coordinate system of the SVG. When width and height are also present, removing viewBox seems harmless. It is not. Without viewBox, an SVG cannot be scaled proportionally by CSS. Setting width: 100% on a container stretches the SVG horizontally but not vertically, producing a distorted or clipped image. This plugin is off by default in SVGO for exactly this reason. Do not enable it.

cleanupIds — off for inline SVG

This plugin rewrites every id attribute to a short hash or sequential identifier to save bytes. That is fine when the SVG is served as a standalone file — nothing outside it references those IDs. It is dangerous when the SVG is inlined in HTML because:

  • JavaScript that queries document.getElementById("my-icon-arrow") will find nothing.
  • CSS rules like #my-icon-arrow { fill: red; } will stop matching.
  • ARIA attributes like aria-labelledby="icon-title" that point to a title element by ID will silently break screen reader associations.
  • Internal references within the SVG — url(#gradient-1) in a fill — are rewritten consistently, but ID collisions across multiple inlined SVGs on the same page become more likely when IDs are shortened to single characters.

Disable cleanupIds whenever the SVG will be inlined in a document where external code may reference its elements.

removeDimensions — a deliberate tradeoff

This plugin removes the explicit width and height attributes, leaving only viewBox. The result is an intrinsically sizeless SVG that stretches to fill its container. This is useful when you always control the size via CSS and want the SVG source to be layout-agnostic. The risk is that the SVG becomes invisible if the container has no defined size — a common mistake in email templates and plain HTML without a stylesheet. Enable it deliberately, not as a default.

Path Data Precision

The floatPrecision parameter of convertPathData (default: 3) controls how many decimal places SVGO keeps in path coordinates, transform matrices, and numeric attribute values. Reducing it saves space; going too low introduces visible distortion.

PrecisionTypical saving vs originalUse case
5–610–20%High-fidelity illustrations, print
3 (default)30–50%Icons, UI graphics, web delivery
1–250–65%Simple geometric shapes only

For icons rendered below 64 px, a precision of 2 is usually indistinguishable from 3. For complex illustrations with curves, drop below 3 only after visual comparison at the intended render size. Always preview optimized SVGs in the SVG Preview tool to catch coordinate-rounding artifacts before deploying.

Inline SVG vs img vs CSS Background

How you deliver an SVG to the browser changes which optimizations are safe and which are counterproductive. The three common delivery methods have meaningfully different constraints.

Inline SVG in HTML

Inline SVG becomes part of the DOM. This enables CSS styling, JavaScript manipulation, and ARIA integration — but it also means:

  • IDs must remain stable if referenced externally — disable cleanupIds.
  • Hidden elements driven by JS must survive — consider disabling removeHiddenElems.
  • Accessibility attributes (title, desc, role, aria-*) must not be stripped. SVGO does not remove these by default, but verify your config does not opt into any plugin that would.
  • Multiple SVGs on the same page share a single ID namespace — prefer cleanupIds off, or use a build tool that namespaces IDs per file.

SVG as an img tag source

When used as <img src="icon.svg">, the SVG is isolated from the document. No external CSS or JS can reach inside it, and its IDs are invisible to the page. This makes most SVGO optimizations fully safe, including cleanupIds. The tradeoff is that you lose the ability to style the icon via CSS custom properties or change its color without duplicating the file.

SVG as a CSS background

CSS backgrounds behave like img in terms of isolation — no DOM access, no inherited CSS. SVGO optimizations are safe. One additional concern: SVG files used as CSS backgrounds must be URL-encoded if inlined as data: URIs. Certain characters — especially #, which appears in color values and fragment identifiers — must be percent-encoded. Some SVGO configurations convert hex colors to shorter forms (e.g., #000000 to #000) which is helpful here.

Accessibility: What You Must Not Strip

SVG has first-class accessibility support. Do not let the optimizer remove it. The elements and attributes that must survive optimization are:

  • title — the accessible name of the SVG, equivalent to alt text on an image. Screen readers announce it. SVGO does not remove it by default, but some community preset configs enable removeTitle for icon sprites where the title is provided externally. Never enable removeTitle on standalone SVGs.
  • desc — a longer description for complex graphics. Pair it with aria-describedby pointing to the desc element's ID.
  • role="img" — tells assistive technology that the SVG is a meaningful image, not decorative markup.
  • aria-label, aria-labelledby, aria-describedby — explicit ARIA relationships that override or supplement the title / desc elements.
  • focusable="false" on decorative SVGs — prevents IE/Edge from tab-stopping on the element.

A correctly accessible inline SVG looks like this in source. Notice that the title ID is referenced by aria-labelledby — if cleanupIds rewrites that ID, the association silently breaks.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
     role="img" aria-labelledby="check-title">
  <title id="check-title">Task complete</title>
  <path fill-rule="evenodd" clip-rule="evenodd"
    d="M20.707 4.293a1 1 0 0 1 0 1.414l-11 11a1 1 0 0 1-1.414 0l-5-5a1 1 0 1 1 1.414-1.414L9 14.586 19.293 4.293a1 1 0 0 1 1.414 0Z"/>
</svg>

Typical Size Savings

Real-world results vary by SVG type and source application, but the following ranges are representative of what SVGO achieves with its default preset:

SVG typeSource appTypical reduction
Simple icon (single path)Figma50–70%
Multi-layer iconIllustrator40–60%
Illustration with gradientsInkscape20–40%
Logo (text + shapes)Any25–45%
Animated SVGAfter Effects / Lottie5–15% (animation data dominates)

For most icon sets, the combination of coordinate rounding, metadata removal, and attribute cleanup produces 50–65% smaller files. An icon set of 200 icons at 800 bytes each becomes roughly 320 bytes each — a saving of 96 KB uncompressed, and still meaningful after gzip because repetitive structure compresses better when there is less of it.

Configuring SVGO: svgo.config.js

The recommended way to configure SVGO is an svgo.config.js file at the project root. It exports an object with a plugins array. You can extend the default preset and selectively override individual plugins.

// svgo.config.js
module.exports = {
  plugins: [
    {
      name: "preset-default",
      params: {
        overrides: {
          // NEVER remove viewBox — it breaks responsive scaling
          removeViewBox: false,

          // Disable for inline SVG that may be referenced by JS/CSS/aria
          cleanupIds: false,

          // Reduce coordinate precision (3 is the default, 2 is safe for icons)
          convertPathData: {
            floatPrecision: 2,
          },

          // Keep hidden elements if JS toggles visibility
          // removeHiddenElems: false,
        },
      },
    },

    // preset-default already strips editor metadata (removeEditorsNSData).
    // Do NOT add removeXMLNS — it deletes the root xmlns and breaks any
    // SVG served standalone (as <img> or a .svg file); only safe inline.

    // Do NOT enable removeTitle — breaks screen reader support
    // Do NOT enable removeDimensions unless CSS always controls size
  ],
};

Running SVGO from the CLI

Install SVGO globally or as a dev dependency, then point it at a file or directory.

# Install
npm install --save-dev svgo

# Optimize a single file (output replaces input)
npx svgo icon.svg

# Optimize and write to a separate output file
npx svgo icon.svg -o icon.min.svg

# Optimize a whole directory
npx svgo --folder src/icons --output dist/icons

# Use a custom config
npx svgo icon.svg --config svgo.config.js

# Dry-run: print result to stdout without writing
npx svgo icon.svg --output -

Integrating SVGO in Your Build

For projects that import SVG files directly in JavaScript (React, Vue, etc.), use a bundler loader rather than running SVGO manually. The loader calls SVGO at build time so the optimized version is always what ships to production.

Vite (vite-plugin-svgo)

// vite.config.js
import { defineConfig } from "vite";
import svgo from "vite-plugin-svgo";

export default defineConfig({
  plugins: [
    svgo({
      // Inline your svgo.config.js options here or let it autodiscover the file
      plugins: [
        {
          name: "preset-default",
          params: { overrides: { removeViewBox: false } },
        },
      ],
    }),
  ],
});

webpack (svgo-loader)

// webpack.config.js (excerpt)
module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        use: [
          { loader: "file-loader" },
          {
            loader: "svgo-loader",
            options: {
              plugins: [
                {
                  name: "preset-default",
                  params: { overrides: { removeViewBox: false } },
                },
              ],
            },
          },
        ],
      },
    ],
  },
};

Both loaders respect svgo.config.js at the project root when no inline configuration is provided, so you can keep a single authoritative config file and reference it from both CLI and build-tool invocations.

If you work with images beyond SVG, the Image Optimization Workflow guide covers raster format choices (AVIF, WebP, JPEG), compression tradeoffs, and how to fit image optimization into a CI pipeline alongside SVGO.


Paste any SVG directly into the SVG Optimizer — it runs SVGO in your browser so your files never leave your machine, and it lets you toggle individual plugins to see exactly what each one removes before committing to a config.