·6 min read
SVG vs PNG for Icons — Which Wins in 2025?
Icons are small, repeated, and critical to UI. Getting the format right saves kilobytes per page load and improves accessibility. Short answer: SVG for UI icons, PNG for favicon fallbacks, ICO for Windows compatibility only.
Why SVG wins for UI icons
- Scalable — one file renders crisp at any DPR (1x, 2x, 3x displays)
- Tiny — a typical UI icon is 200-500 bytes vs 1-3 KB for PNG
- Styleable with CSS — change color via
currentColor, animate via transform - Accessible — inline SVG gets a
<title>for screen readers
Download sample SVGs to test your icon pipeline.
Inline SVG (the best pattern)
<button aria-label="Delete">
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
<title>Delete</title>
<path d="M6 19a2 2 0 002 2h8a2 2 0 002-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" />
</svg>
</button>
Inline SVG inherits color from its parent via fill="currentColor", meaning hover/focus states work automatically.
When PNG makes sense
- Photographic icons — app store screenshots, product thumbnails
- Favicons — serve 32×32 PNG for modern browsers, plus ICO for old Windows
- Complex gradients — sometimes cheaper as raster than SVG
Modern favicon setup
<!-- Modern browsers: SVG, auto-scales -->
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
<!-- Legacy Windows: ICO fallback -->
<link rel="icon" href="/favicon.ico" sizes="any" />
<!-- iOS home screen -->
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
File size reality check
Heart icon at 24×24:
- SVG (optimized via SVGO) — 204 bytes
- PNG at 2x (48×48) — 1.2 KB
- PNG at 3x (72×72) — 2.4 KB (for retina)
Ten icons in a navbar: SVG ~2 KB total, PNG ~24 KB total.
Optimization tools
- SVGO — removes editor metadata, shrinks SVG by 30-70%
- oxipng / zopflipng — lossless PNG compression
- SVGR — convert SVG to React components with proper props forwarding
Accessibility
{/* Decorative icon — hide from screen readers */}
<svg aria-hidden="true" focusable="false">...</svg>
{/* Meaningful icon — give it a label */}
<button aria-label="Close dialog">
<svg aria-hidden="true">...</svg>
</button>
{/* Standalone icon with text in SVG */}
<svg role="img" aria-labelledby="t1">
<title id="t1">User settings</title>
...
</svg>
Related
For the general format comparison, see WebP vs PNG vs JPG. For image loading strategy, read image optimization for web performance.