🍱 Lunchbox Hands

svg

SVG Optimizer vs SVG to PNG vs SVG to React: Which Conversion Does Your SVG Actually Need?

A decision guide for the three things you can do with an SVG — minify it with SVGO, rasterize it to PNG, or convert it to a React component — plus when to go the other way with PNG-to-SVG tracing. With email-client support data and the honest trade-offs of each route.

You’ve got an SVG — a logo from the designer, an icon from Figma, a chart export — and three tools that all claim to make it “ready for production”: an optimizer, a PNG converter, and a React converter. They’re not competing answers to one question. An SVG is source code; a PNG is a build artifact compiled from it; a React component is that source inlined into your render tree — so the right conversion depends entirely on where the image has to live, not on which output is “better.” Pick by destination and the choice is mechanical. Pick by habit and you end up inlining 200 icons into your bundle, or emailing an SVG that Gmail silently refuses to show.

Here’s the destination-by-destination breakdown.

The mental model: one source, three destinations

Treat the SVG file the way you treat a .ts file:

  • Ship the source — the browser renders SVG natively, so for the web you can serve the file as-is (just minified). Infinitely scalable, tiny for geometric art, cacheable.
  • Compile to pixels — some destinations (email clients, favicon slots, social-card scrapers) don’t reliably execute SVG. For those you rasterize: fix a size, export a PNG, accept that scaling is gone.
  • Inline into the app — when the image needs to participate in your component tree (theme colors, props, animation state), you convert the markup to JSX and it stops being an asset at all.

Every “should I…?” question about SVGs is one of these three, plus one reverse gear we’ll get to at the end.

Destination 1: the web, as an image — optimize and ship the SVG

If the SVG is going into an <img> tag, a CSS background-image, or an inline <svg> block on a page, don’t convert anything — the vector is the deliverable. What it needs is minification, because editor exports are bloated: Illustrator, Figma, and Sketch pad files with XML declarations, editor metadata, comments, unused namespaces, and coordinates carried to a dozen decimal places.

That’s what an SVGO pass removes. The SVG Optimizer runs real SVGO (its browser build, client-side) with preset-default and multipass enabled, and shows before/after byte counts plus a visual diff so you can confirm nothing changed. Typical export cruft looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generator: Adobe Illustrator 27.0.0, SVG Export Plug-In -->
<svg xmlns:xlink="http://www.w3.org/1999/xlink" ...>
  <path d="M12.000000,3.141592 L18.500000,9.007000 ..."/>

None of that survives, and the rendered image is unchanged. Savings of 30–70% are routine on editor exports, which matters most for inline SVG — that markup rides along in your HTML on every page load and feeds directly into page weight and Largest Contentful Paint.

The honest caveats. “Lossless” deserves an asterisk, on two counts:

  1. Precision. SVGO’s preset-default includes cleanupNumericValues and convertPathData, and both round coordinates to 3 decimal places by default (that’s the floatPrecision = 3 default in SVGO’s source). At 3 digits you will essentially never see a difference — but if you or a build pipeline crank precision down to 0–1 to chase bytes, complex curves visibly wobble. Rounding is the one genuinely lossy knob in the default preset.
  2. IDs. cleanupIds (also in preset-default) removes IDs that nothing inside the file references and minifies the rest. SVGO can’t see your external CSS or JavaScript — so if you animate #gear-left from a stylesheet, the optimized file may no longer contain #gear-left. If you hook into an SVG by ID or class, re-test those hooks after optimizing.

Destination 2: email, favicons, and social cards — rasterize to PNG

Some destinations simply don’t speak SVG, and no amount of optimizing helps. The big three:

Email. This is the clearest case. Per caniemail, inline <svg> scores about 40% support across email clients — and Gmail is a hard “no” on every platform: desktop webmail, iOS, Android, and mobile webmail all strip embedded SVG. Outlook (Windows, Outlook.com, iOS, Android) rejects it too. Even SVG as a linked image fails where it counts: Gmail’s desktop webmail won’t display linked .svg files (still unsupported in caniemail’s 2024 tests), and the Gmail mobile apps only show them for non-Google accounts. For anything in an email template, export a PNG at 2x and stop fighting.

Favicons and social cards. Platforms that consume your images as data — favicon slots, Open Graph scrapers, app-icon pipelines — overwhelmingly expect raster at fixed dimensions. Social scrapers want a 1200×630 raster image (details in the Open Graph image size guide), and while modern browsers accept an SVG favicon, the full favicon/touch-icon matrix still needs PNGs (see the favicon sizes guide).

Tiny fixed-size renders of complex art. Vector cost scales with path complexity, not display size. A 90 KB illustrated mascot rendered at 48×48 pixels is a bad trade — a PNG of that mascot at 96×96 might be 4 KB. When the display size is small and fixed and the artwork is complex, pixels are cheaper than paths.

The SVG to PNG converter does this in-browser with the Canvas API: paste SVG, pick 1x, 2x, or 3x scale (the multiplier applies to the SVG’s intrinsic size — use 2x/3x for retina), preview, download. Once you’re in raster land, the PNG vs JPG vs WebP vs AVIF guide covers which raster format to actually ship — for the sharp edges and transparency typical of rasterized vectors, PNG or lossless WebP is usually right.

Destination 3: your component tree — convert to React

Serving <img src="icon.svg"> has one hard limitation: the image lives in a separate document, so your page’s CSS can’t reach inside it. color: red on the parent does nothing; you can’t swap a stroke on hover or bind a fill to a theme token. The moment an SVG needs to react — currentColor theming, props, animation driven by state — it should become a component.

The SVG to React converter does the mechanical part: attributes are mapped to JSX camelCase (stroke-widthstrokeWidth, classclassName, xlink:hrefxlinkHref), inline style strings become style objects, and props are spread onto the root element so callers can pass className, width, or event handlers. Options cover TypeScript output (typed as React.FC<React.SVGProps<SVGSVGElement>>), React.memo wrapping, named vs default export, and a currentColor switch that replaces hard-coded fills and strokes so the icon inherits text color:

const Icon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
  <svg {...props} viewBox="0 0 24 24" fill="none">
    <path d="M12 3v18M3 12h18" stroke="currentColor" strokeWidth="2" />
  </svg>
)

// Now it themes and sizes like text:
<Icon className="h-5 w-5 text-red-500" onClick={toggle} />

The honest cost. Every inlined component is markup in your JavaScript bundle. A static .svg file is downloaded once, cached by the browser, and shared across pages; an inlined icon is parsed as JS, shipped inside your bundle, and duplicated into the DOM at every render site. For a handful of interactive icons that’s obviously fine — and tree-shaking means you only pay for the icons you import. But converting a 300-icon set wholesale trades a pile of cacheable static assets for permanent bundle weight. The rule of thumb: inline what needs to be styled or animated; serve the rest as optimized .svg files. Both halves of that rule are doing work.

One nice interaction: run the SVG through the optimizer first, then convert — less markup in, less JSX out, smaller bundle contribution.

The reverse gear: PNG to SVG (tracing)

Sometimes you only have the raster — a logo screenshot, a scanned signature — and want the vector back. That’s what PNG to SVG does: it traces regions of similar color into paths, in-browser (using imagetracerjs, with presets for color logos, posterized art, and black-and-white line work).

Know its limits before reaching for it. Tracing works when the image is secretly a vector already — logos, icons, line art, flat illustration with a handful of clean color regions. It fails on photographs, by construction: a photo’s gradients and noise mean millions of subtly different colors, so the tracer either posterizes the image or emits tens of thousands of tiny paths — an SVG that’s simultaneously larger than the PNG and worse-looking. The tool shows an honest before/after size comparison so you can see which side of the line your image landed on. When a trace does succeed, the output carries redundant markup — close the loop by running it through the SVG Optimizer.

The decision table

Where the image is goingDo thisToolWatch out for
<img>, CSS background, inline on a pageKeep SVG, minify itSVG OptimizerIDs/classes referenced by external CSS/JS; aggressive precision settings
Email templateRasterize (2x for retina)SVG to PNGGmail strips inline SVG and won’t show linked .svg in webmail
Favicon, OG/social card, app iconRasterize at required sizesSVG to PNGEach slot wants specific pixel dimensions
Small fixed size, complex artworkRasterize — pixels beat paths hereSVG to PNGCompare file sizes; the vector often loses
Needs theming, props, or state-driven animationConvert to a componentSVG to ReactBundle weight — inline the interactive icons, not the whole set
Decorative icon in a React app, no styling neededOptimized .svg via <img>SVG OptimizerNothing — this is the cheap, cacheable default
You only have a raster of flat artTrace it back to vectorPNG to SVGLogos and line art only; photos trace into huge, ugly files

The short version

  • SVG is the source. Keep it whenever the destination renders SVG — which is every modern browser — and let the optimizer strip the editor cruft (default SVGO rounds to 3 decimals and prunes unreferenced IDs; re-check external CSS/JS hooks).
  • Rasterize when the destination demands pixels: email (Gmail renders neither inline nor linked SVG in webmail), favicons, social cards, and tiny fixed-size renders of complex art — SVG to PNG at 2x covers retina.
  • Componentize when the image joins your UI logic: currentColor, props, animation — SVG to React — and accept that inlined icons are bundle weight, so don’t convert sets wholesale.
  • Trace raster to vector only for flat artPNG to SVG — never photos.

Same file, three destinations, four tools. Match the conversion to where the image lives and every one of these choices makes itself.