SVG looks intimidating at first glance — it's XML, it has a coordinate system, there's math involved — but once you understand the mental model, it becomes remarkably practical. It's the only image format on the web that's also a programming language.
What SVG Actually Is
SVG stands for Scalable Vector Graphics. Unlike JPEG or PNG, which store color values for a grid of pixels, SVG stores a description of shapes: "draw a circle at this position with this radius," "draw a path following these coordinates." The browser's rendering engine interprets that description and draws the result at whatever size you ask for.
That "at whatever size" part is the key property. A pixel-based image has a fixed resolution — enlarge it past its native size and you see blurriness or blocky pixels. An SVG renders at device resolution regardless of display size. A 24×24px icon looks identical on a standard screen and a 3x Retina display. Your logo scales from a 16px favicon to a billboard with no quality loss.
SVG is XML. Two important consequences: you can write it by hand, and you can manipulate it with code.
The viewBox and Coordinate System
The viewBox attribute defines the internal coordinate space of an SVG. It takes four values: min-x min-y width height.
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<!-- coordinate space is 0-100 in both dimensions -->
<circle cx="50" cy="50" r="40" fill="royalblue" />
</svg>
This SVG has an internal coordinate space of 100×100 units. The circle is centered at (50,50) with a radius of 40 units. You can display this SVG at any size — 24px, 200px, 2000px — and the circle will always be centered and fill ~80% of the bounding box.
The viewBox decouples the internal coordinate system from display size. Set width="100%" or width="24" on the <svg> element and the browser scales the viewBox to fit.
The Key Elements
path
path is the most powerful and most common SVG element. It uses a mini-language of commands to describe arbitrary shapes.
<path d="M 10 10 L 90 10 L 90 90 L 10 90 Z" fill="none" stroke="black" />
Commands:
M x y— Move to (x, y), pen upL x y— Line to (x, y)C x1 y1 x2 y2 x y— Cubic bezier curveA rx ry rotation large-arc sweep x y— Elliptical arcZ— Close path (line back to start)
Lowercase versions (m, l, c) use relative coordinates instead of absolute. Most SVG icons are entirely composed of path elements — it's what export tools like Figma and Illustrator generate.
Basic shapes
SVG has convenience elements for common shapes:
<rect x="10" y="10" width="80" height="80" rx="8" fill="coral" />
<circle cx="50" cy="50" r="40" fill="gold" />
<ellipse cx="50" cy="50" rx="45" ry="30" fill="lightblue" />
<line x1="10" y1="10" x2="90" y2="90" stroke="black" stroke-width="2" />
<polyline points="10,10 50,50 90,10" fill="none" stroke="purple" />
<polygon points="50,10 90,90 10,90" fill="tomato" />
rx on a <rect> rounds the corners — the equivalent of CSS border-radius.
text
Text in SVG is actual, selectable, searchable text — not rasterized into pixels.
<text x="50" y="55" text-anchor="middle" font-family="system-ui" font-size="14">
Hello, SVG
</text>
This is why SVG is excellent for charts, diagrams, and infographics. The labels remain accessible and selectable rather than being baked into an image.
g and defs
<g> groups elements together, allowing transforms and styles to apply to the whole group. <defs> defines reusable elements (gradients, filters, patterns, symbols) that are referenced elsewhere.
<defs>
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#f5c842" />
<stop offset="100%" stop-color="#fb923c" />
</linearGradient>
</defs>
<rect width="100" height="40" fill="url(#grad)" />
<symbol> defines a reusable shape that can be instantiated with <use>:
<defs>
<symbol id="icon-check" viewBox="0 0 16 16">
<path d="M2 8 L6 12 L14 4" stroke="currentColor" stroke-width="2" fill="none" />
</symbol>
</defs>
<use href="#icon-check" width="16" height="16" />
<use href="#icon-check" width="32" height="32" x="20" />
Inline SVG vs img src vs CSS Background
How you embed an SVG changes what you can do with it.
Inline SVG (<svg> directly in HTML) gives you full CSS and JavaScript access to every element inside. You can target individual paths, animate them, change colors based on state, respond to hover and click events on specific parts of an icon.
<button class="btn">
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
<path d="..." fill="currentColor" />
</svg>
Save
</button>
The currentColor value inherits the CSS color property from the parent element. Change the button's text color, the icon follows. This is the standard approach for UI icons.
<img src="icon.svg"> loads the SVG as an external document. It renders and scales correctly, but you have no access to the SVG's internals — you can't change colors or animate paths from external CSS.
CSS background image (background-image: url('icon.svg')) has the same limitation. No access to internals, no currentColor inheritance. Useful for decorative backgrounds but limited for interactive UI elements.
Rule of thumb: inline SVG for icons that need to respond to state or inherit color; <img> for standalone illustrations; background image for decorative patterns.
Styling SVG with CSS
When SVG is inline, CSS applies normally:
.icon {
fill: currentColor; /* inherit from parent */
stroke: none;
transition: fill 0.2s ease;
}
button:hover .icon {
fill: var(--accent);
}
fill and stroke are SVG-specific CSS properties. fill is the interior color of shapes. stroke is the outline color. stroke-width, stroke-linecap, stroke-linejoin control how strokes render.
currentColor is the key trick for themeable icons. It lets one SVG work correctly in dark mode, light mode, on colored backgrounds, and in disabled states without any JavaScript.
Animating SVG
Two approaches exist, and they're not equivalent.
CSS animations work on SVG elements the same way they work on HTML elements:
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spinner {
transform-origin: center;
animation: spin 1s linear infinite;
}
SMIL (Synchronized Multimedia Integration Language) is SVG's built-in animation syntax:
<circle cx="50" cy="50" r="40">
<animate
attributeName="r"
values="40;20;40"
dur="2s"
repeatCount="indefinite" />
</circle>
SMIL was deprecated by Chrome, then un-deprecated, and remains in a gray zone. For new work, CSS animations are the safer choice — better tooling support, easier to control with JavaScript, and no dependency on SVG-specific parser behavior.
For complex animations, GreenSock (GSAP) is the standard library used by most production SVG animation work.
Optimizing SVGs with SVGO
SVG files exported from Figma, Illustrator, or Sketch contain significant bloat: editor metadata, unused definitions, redundant coordinates, verbose path data that could be simplified.
SVGO (SVG Optimizer) is the standard tool for cleaning this up:
# Install
npm install -g svgo
# Optimize a file (overwrites in place)
svgo icon.svg
# Optimize a file and write to a new path
svgo icon.svg -o icon.min.svg
# Optimize all SVGs in a directory
svgo ./icons/*.svg
# List available plugins
svgo --help
Typical savings are 20–50% of file size with no visual change. On a large icon library, this adds up significantly.
SVG as an Icon System
Instead of individual SVG files per icon, many teams build a sprite system: a single SVG file containing all icons as <symbol> elements, with each <use> tag referencing the appropriate symbol by ID.
<!-- Single sprite file included once in the document -->
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<symbol id="icon-home" viewBox="0 0 24 24">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
</symbol>
<symbol id="icon-search" viewBox="0 0 24 24">
<circle cx="11" cy="11" r="8" />
<line x1="21" y1="21" x2="16.65" y2="16.65" />
</symbol>
</svg>
<!-- Use icons anywhere -->
<svg width="24" height="24"><use href="#icon-home" /></svg>
<svg width="24" height="24"><use href="#icon-search" /></svg>
This approach serves all icons in a single HTTP request, keeps individual icon references small, and allows currentColor to work for each <use> instance.
When SVG vs Raster
Use SVG when the content is geometric or illustrative, needs to scale to multiple sizes, needs to respond to state changes via CSS, or contains text that should remain selectable. Logos, icons, charts, diagrams, maps, UI illustrations.
Use raster (WebP, AVIF, JPEG) when the content is photographic, contains complex textures, or was captured rather than drawn. Photographs, product images, screenshots of complex UIs.
These formats aren't competing for the same use cases. They answer different questions.
Wrapping Up
SVG's combination of infinite scalability, CSS integration, and text-based format makes it the right choice for anything that isn't a photograph. The SVG specification at W3C is thorough and well-organized. If you need to convert raster images to work alongside your SVGs, Image Convert and Image Compress handle the raster side of the equation. For a broader look at how SVG fits alongside JPEG, PNG, and WebP, read Image Formats Explained.