What Is SVG? The Scalable Vector Graphics Format Explained
SVG (Scalable Vector Graphics) is an XML-based image format that describes graphics using mathematical shapes rather than pixels. Because SVG is resolution-independent, a single file looks sharp on a mobile screen, a 4K monitor, or a billboard — with zero quality loss at any size.
SVG is the native format for icons, logos, illustrations, charts, and UI graphics on the modern web. Every major browser renders SVG natively, and it can be inlined directly into HTML.
SVG vs Raster Formats (PNG, JPG, WebP)
| SVG | PNG | JPG | WebP | |
|---|---|---|---|---|
| Encoding | Vector (math) | Raster (pixels) | Raster (pixels) | Raster (pixels) |
| Scales without blur | ✅ Yes | ❌ No | ❌ No | ❌ No |
| Transparency | ✅ Yes | ✅ Yes | ❌ No | ✅ Yes |
| Animation | ✅ Built-in (SMIL/CSS) | ❌ No | ❌ No | ❌ No |
| Editable with CSS/JS | ✅ Yes | ❌ No | ❌ No | ❌ No |
| Best for | Icons, logos, charts | Screenshots, UI | Photos | Web photos |
| Worst for | Photos, complex images | Large illustrations | Text, sharp edges | Vector art |
Rule of thumb: use SVG for anything you draw (icons, logos, diagrams, charts); use raster formats for photographs and complex textures.
SVG File Structure
An SVG file is plain XML. Open any .svg in a text editor and you'll see something like this:
<svg xmlns="http://www.w3.org/2000/svg"
width="100" height="100"
viewBox="0 0 100 100">
<!-- A red circle -->
<circle cx="50" cy="50" r="40"
fill="red" stroke="black" stroke-width="2" />
<!-- A blue rectangle -->
<rect x="10" y="60" width="30" height="20"
fill="blue" opacity="0.5" />
<!-- Text -->
<text x="50" y="95" text-anchor="middle"
font-size="10" fill="white">Hello</text>
</svg>
Core SVG Elements
| Element | Purpose | Key attributes |
|---|---|---|
<svg> |
Root container | width, height, viewBox |
<rect> |
Rectangle | x, y, width, height, rx (rounded corners) |
<circle> |
Circle | cx, cy, r |
<ellipse> |
Ellipse | cx, cy, rx, ry |
<line> |
Straight line | x1, y1, x2, y2 |
<polyline> |
Open polygon | points |
<polygon> |
Closed polygon | points |
<path> |
Arbitrary shape | d (path data — M/L/C/A/Z commands) |
<text> |
Text | x, y, font-size, fill |
<g> |
Group | id, transform |
<use> |
Reuse element | href |
<defs> + <symbol> |
Define reusable assets | — |
The viewBox Attribute — Why It Matters
viewBox="minX minY width height" defines the internal coordinate system. The outer width/height attributes set the rendered size in the browser.
<!-- 24×24 coordinate space, rendered at 48×48px (2× sharp on retina) -->
<svg viewBox="0 0 24 24" width="48" height="48">
<path d="M12 2L2 22h20L12 2z" />
</svg>
Omit width/height and the SVG will scale to fill its CSS container — a common technique for responsive icons.
Generating SVG Programmatically
JavaScript (Browser + Node)
Browser — modify the live DOM:
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('viewBox', '0 0 100 100');
svg.setAttribute('width', '100');
svg.setAttribute('height', '100');
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('cx', '50');
circle.setAttribute('cy', '50');
circle.setAttribute('r', '40');
circle.setAttribute('fill', 'steelblue');
svg.appendChild(circle);
document.body.appendChild(svg);
Node.js — generate SVG string:
function makeBadge(label, value) {
return `<svg xmlns="http://www.w3.org/2000/svg" width="120" height="20" viewBox="0 0 120 20">
<rect width="60" height="20" fill="#555"/>
<rect x="60" width="60" height="20" fill="#4c1"/>
<text x="30" y="14" fill="#fff" font-size="11"
text-anchor="middle" font-family="sans-serif">${label}</text>
<text x="90" y="14" fill="#fff" font-size="11"
text-anchor="middle" font-family="sans-serif">${value}</text>
</svg>`;
}
const fs = require('fs');
fs.writeFileSync('badge.svg', makeBadge('build', 'passing'));
Python
# pip install svgwrite
import svgwrite
def make_chart(data: list[int], filename: str = "chart.svg") -> None:
"""Generate a simple bar chart as SVG."""
width, height = 300, 200
bar_width = width / len(data)
max_val = max(data)
dwg = svgwrite.Drawing(filename, size=(width, height))
dwg.add(dwg.rect((0, 0), (width, height), fill="white", stroke="black"))
for i, val in enumerate(data):
bar_h = (val / max_val) * (height - 20)
x = i * bar_width
y = height - bar_h
dwg.add(dwg.rect(
insert=(x + 2, y),
size=(bar_width - 4, bar_h),
fill="steelblue"
))
dwg.add(dwg.text(
str(val),
insert=(x + bar_width / 2, y - 2),
text_anchor="middle",
font_size="10px",
fill="black"
))
dwg.save()
make_chart([40, 80, 60, 100, 55, 75])
Parse an existing SVG (stdlib only):
import xml.etree.ElementTree as ET
tree = ET.parse('icon.svg')
root = tree.getroot()
ns = {'svg': 'http://www.w3.org/2000/svg'}
# Find all circles
for circle in root.findall('.//svg:circle', ns):
print(circle.attrib) # {'cx': '50', 'cy': '50', 'r': '40', ...}
Go
package main
import (
"fmt"
"os"
"text/template"
)
type BarChart struct {
Width, Height int
Bars []Bar
}
type Bar struct {
X, Y, W, H float64
Label string
Fill string
}
const svgTmpl = `<svg xmlns="http://www.w3.org/2000/svg"
width="{{.Width}}" height="{{.Height}}"
viewBox="0 0 {{.Width}} {{.Height}}">
<rect width="{{.Width}}" height="{{.Height}}" fill="white" stroke="#ccc"/>
{{- range .Bars}}
<rect x="{{.X}}" y="{{.Y}}" width="{{.W}}" height="{{.H}}" fill="{{.Fill}}"/>
<text x="{{printf "%.1f" (add .X (div .W 2.0))}}"
y="{{printf "%.1f" (sub .Y 4.0)}}"
text-anchor="middle" font-size="10" fill="#333">{{.Label}}</text>
{{- end}}
</svg>`
func main() {
tmpl := template.Must(template.New("svg").Funcs(template.FuncMap{
"add": func(a, b float64) float64 { return a + b },
"sub": func(a, b float64) float64 { return a - b },
"div": func(a, b float64) float64 { return a / b },
}).Parse(svgTmpl))
chart := BarChart{
Width: 300,
Height: 200,
Bars: []Bar{
{X: 10, Y: 60, W: 60, H: 130, Label: "Jan", Fill: "steelblue"},
{X: 80, Y: 40, W: 60, H: 150, Label: "Feb", Fill: "tomato"},
{X: 150, Y: 80, W: 60, H: 110, Label: "Mar", Fill: "seagreen"},
},
}
f, _ := os.Create("chart.svg")
defer f.Close()
tmpl.Execute(f, chart)
fmt.Println("chart.svg written")
}
PHP
<?php
function makeSvgCircle(float $cx, float $cy, float $r, string $fill): string {
$cx = htmlspecialchars((string)$cx, ENT_XML1);
$cy = htmlspecialchars((string)$cy, ENT_XML1);
$r = htmlspecialchars((string)$r, ENT_XML1);
$fill = htmlspecialchars($fill, ENT_XML1);
return <<<SVG
<svg xmlns="http://www.w3.org/2000/svg"
width="200" height="200"
viewBox="0 0 200 200">
<circle cx="{$cx}" cy="{$cy}" r="{$r}"
fill="{$fill}" stroke="black" stroke-width="2"/>
</svg>
SVG;
}
// Write to file
$svg = makeSvgCircle(100, 100, 80, 'coral');
file_put_contents('circle.svg', $svg);
// Parse SVG with SimpleXML
$xml = simplexml_load_file('circle.svg');
$xml->registerXPathNamespace('svg', 'http://www.w3.org/2000/svg');
$circles = $xml->xpath('//svg:circle');
foreach ($circles as $c) {
echo "Circle: cx={$c['cx']} cy={$c['cy']} r={$c['r']}\n";
}
Optimizing SVG Files
SVGs exported from Illustrator, Figma, or Inkscape carry a lot of metadata, editor comments, and redundant attributes. Optimization typically shrinks file size by 30–70%.
SVGO (Node.js):
npx svgo input.svg -o output.svg
# Or optimize all SVGs in a directory:
npx svgo --folder ./icons
Key things SVGO removes:
- Editor metadata (
<sodipodi:*>,<inkscape:*>) - Comments and
<!-- --> - Redundant
idattributes - Default attribute values (e.g.,
fill="black") - Empty groups
<g></g> - Precision: rounds
0.99999999→1
Python (scour):
pip install scour
scour -i input.svg -o output.svg --enable-id-stripping --shorten-ids
Inline SVG vs External <img>
Inline <svg> |
<img src="icon.svg"> |
CSS background-image |
|
|---|---|---|---|
| CSS/JS access | ✅ Full DOM access | ❌ None | ❌ None |
| Animation | ✅ CSS + JS | ✅ SMIL only | ✅ CSS only |
| Caching | ❌ Not cached | ✅ Cached | ✅ Cached |
| ARIA / accessibility | ✅ aria-label, <title> |
✅ alt attribute |
❌ Poor |
| Best for | Interactive icons, themed UI | Static illustrations | Decorative backgrounds |
Quick Reference
| Task | Tool / Method |
|---|---|
| Draw SVG by hand | Any text editor |
| Design SVG visually | Figma, Inkscape, Illustrator |
| Optimize SVG | svgo, scour |
| Convert SVG → PNG/JPG | Toolko Image Converter |
| Inline SVG in React | <svg> tags directly, or import { ReactComponent } |
| Animate SVG | CSS @keyframes on SVG elements, or GSAP |
| Parse SVG in code | xml.etree (Python), encoding/xml (Go), SimpleXML (PHP), DOMParser (JS) |
6 Common Mistakes
Missing
xmlnson exported SVG. If you generate SVG in code and serve it as a standalone file, includexmlns="http://www.w3.org/2000/svg"on the root element — omitting it breaks rendering in some contexts.Forgetting
viewBox. WithoutviewBox, the SVG won't scale responsively. Always setviewBox="0 0 W H"where W×H matches your design coordinates.Using SVG for photographs. SVG traces of photos produce huge files and poor quality. Use JPG, WebP, or AVIF instead.
Unoptimized exports from design tools. Figma exports typically add 2–5× unnecessary data. Always run SVGO before shipping to production.
Injecting untrusted SVG content. SVG supports
<script>tags and event handlers (onclick,onload). Never render user-uploaded SVG directly in the page without sanitizing it — this is an XSS vector.Hardcoding
width/heightwhen you want responsive scaling. Removewidthandheightfrom the root<svg>and let CSS control the size. KeepviewBoxso the proportions are preserved.
Frequently Asked Questions
Can I open an SVG in Photoshop or GIMP?
Yes, but both apps rasterize the SVG to pixels at import. You lose the vector properties. Use Inkscape (free) or Illustrator to edit SVG as vectors.
Why is my SVG blurry when I export it to PNG?
You're likely exporting at 1× screen resolution. Set the export DPI to 144 (2×) or 216 (3×) to get a sharp PNG for high-DPI screens.
Is SVG supported in all browsers?
Yes — all modern browsers (Chrome, Firefox, Safari, Edge) support SVG fully. IE 11 supports SVG but lacks some CSS features. IE 9/10 support basic SVG.
Can I use SVG in an <img> tag?
Yes. <img src="icon.svg" alt="..."> works in all modern browsers. Scripts and external stylesheets inside the SVG are blocked for security.
How do I make an SVG icon change colour on hover?
Inline the SVG in HTML and use CSS: svg:hover path { fill: red; }. This requires the icon to use fill="currentColor" or no fill attribute (so CSS can override it).
What is the difference between SVG and Canvas?
SVG is a retained-mode, DOM-based API — each shape is an element you can inspect and animate with CSS. Canvas is immediate-mode — you paint pixels and the DOM knows nothing about them. SVG is better for interactive diagrams and UI icons; Canvas is better for games, real-time visualizations, and pixel manipulation.