What Is SVG?
SVG (Scalable Vector Graphics) is an XML-based vector image format developed by the W3C and standardized as SVG 1.1 (2003) and SVG 2 (working draft). Unlike raster formats (PNG, JPEG, WebP) which store pixel grids, SVG stores geometric descriptions of shapes — coordinates, curves, colors, transforms — that can be rendered at any resolution without quality loss.
An SVG file is plain text XML that can be:
- Opened and edited in any text editor
- Embedded directly in HTML (inline SVG)
- Referenced as an
<img>, CSSbackground-image, or<object>tag - Styled with CSS, scripted with JavaScript, and animated with CSS or SMIL
- Indexed by search engines (SVG text is readable text)
- Printed at any scale without pixelation
SVG is used for icons, logos, illustrations, data visualizations (D3.js), UI elements, maps, diagrams, and increasingly as an animation platform.
SVG Document Structure
An SVG file is a well-formed XML document:
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="200" height="200"
viewBox="0 0 200 200"
role="img"
aria-label="Blue circle with red border">
<title>Blue Circle</title>
<desc>A filled blue circle with a 3px red border</desc>
<circle cx="100" cy="100" r="80"
fill="#3B82F6" stroke="#EF4444" stroke-width="3"/>
</svg>
Key SVG Attributes
| Attribute | Description |
|---|---|
xmlns |
SVG namespace (required for standalone files) |
width, height |
Dimensions (px, em, %, or unitless) |
viewBox="x y width height" |
Internal coordinate system |
preserveAspectRatio |
How viewBox maps to viewport |
role, aria-label |
Accessibility attributes |
The viewBox
The viewBox defines the SVG's internal coordinate space, decoupling it from rendered size:
<svg width="400" height="400" viewBox="0 0 100 100">
<!-- coordinate (50,50) is the center regardless of rendered size -->
<circle cx="50" cy="50" r="40" fill="blue"/>
</svg>
This means the SVG renders as 400×400 pixels, but internally everything is drawn in a 100×100 coordinate space. The browser scales automatically. This is what makes SVG "scalable" — the same file renders perfectly at 16×16 (favicon) or 4096×4096.
Basic Shape Elements
Geometric Primitives
<!-- Rectangle -->
<rect x="10" y="10" width="80" height="60" rx="5" ry="5"/>
<!-- Circle -->
<circle cx="50" cy="50" r="30"/>
<!-- Ellipse -->
<ellipse cx="50" cy="50" rx="40" ry="25"/>
<!-- Line -->
<line x1="0" y1="0" x2="100" y2="100" stroke="black" stroke-width="2"/>
<!-- Polyline (open path through multiple points) -->
<polyline points="10,10 50,50 90,10 90,90" fill="none" stroke="blue"/>
<!-- Polygon (closed path) -->
<polygon points="50,10 90,90 10,90" fill="yellow" stroke="orange"/>
The Path Element — Most Powerful SVG Element
The <path> element uses a mini-language of path commands:
<path d="M 10,10 L 90,10 L 90,90 L 10,90 Z"/>
<!-- M=Move to, L=Line to, Z=Close path — draws a square -->
<path d="M 10,50 C 10,10 90,10 90,50"/>
<!-- C=Cubic Bezier curve — draws an arch -->
<path d="M 50,10 A 40,40 0 1,0 50,90"/>
<!-- A=Arc — draws a semicircle -->
Complete Path Command Reference
| Command | Uppercase (absolute) | Lowercase (relative) | Parameters |
|---|---|---|---|
| Move to | M x,y |
m dx,dy |
Target point |
| Line to | L x,y |
l dx,dy |
Target point |
| Horizontal line | H x |
h dx |
X coordinate only |
| Vertical line | V y |
v dy |
Y coordinate only |
| Cubic Bezier | C x1,y1 x2,y2 x,y |
c |
Two control points + endpoint |
| Smooth cubic | S x2,y2 x,y |
s |
One control point + endpoint |
| Quadratic Bezier | Q x1,y1 x,y |
q |
One control point + endpoint |
| Smooth quadratic | T x,y |
t |
Endpoint only |
| Arc | A rx,ry rot large-arc sweep x,y |
a |
Elliptical arc parameters |
| Close path | Z |
z |
None |
Uppercase = absolute coordinates (from SVG origin). Lowercase = relative (from current point).
Text in SVG
<text x="50" y="50" font-family="Arial" font-size="16" fill="#333"
text-anchor="middle" dominant-baseline="middle">
Hello, SVG!
</text>
<!-- Multi-line text with tspan -->
<text x="10" y="30" font-size="14">
<tspan x="10" dy="0">First line</tspan>
<tspan x="10" dy="20">Second line</tspan>
<tspan x="10" dy="20" font-weight="bold">Bold third line</tspan>
</text>
<!-- Text on a path -->
<defs>
<path id="curve" d="M 10,80 C 40,10 60,10 90,80"/>
</defs>
<text>
<textPath href="#curve">Text follows the curve</textPath>
</text>
SVG text is actual text — searchable, selectable, accessible, and indexable by search engines. This makes SVG ideal for logos and icons that include text.
Gradients, Filters, and Clip Paths
Gradients
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#3B82F6; stop-opacity:1"/>
<stop offset="100%" style="stop-color:#8B5CF6; stop-opacity:1"/>
</linearGradient>
<radialGradient id="grad2" cx="50%" cy="50%" r="50%">
<stop offset="0%" style="stop-color:white; stop-opacity:1"/>
<stop offset="100%" style="stop-color:blue; stop-opacity:0"/>
</radialGradient>
</defs>
<rect width="200" height="100" fill="url(#grad1)"/>
<circle cx="50" cy="50" r="40" fill="url(#grad2)"/>
Filters (SVG Filter Primitives)
<defs>
<filter id="blur">
<feGaussianBlur stdDeviation="4"/>
</filter>
<filter id="shadow">
<feDropShadow dx="4" dy="4" stdDeviation="4" flood-color="#00000055"/>
</filter>
<filter id="glow">
<feGaussianBlur stdDeviation="4" result="blur"/>
<feComposite in="SourceGraphic" in2="blur" operator="over"/>
</filter>
</defs>
<circle cx="50" cy="50" r="40" fill="blue" filter="url(#blur)"/>
<rect x="10" y="10" width="80" height="80" fill="orange" filter="url(#shadow)"/>
Available filter primitives: feBlend, feColorMatrix, feComposite, feConvolveMatrix, feDiffuseLighting, feDisplacementMap, feDropShadow, feFlood, feGaussianBlur, feMerge, feMorphology, feOffset, feSpecularLighting, feTurbulence.
Clip Paths and Masks
<defs>
<clipPath id="circle-clip">
<circle cx="50" cy="50" r="40"/>
</clipPath>
<mask id="fade-mask">
<linearGradient id="fade" x1="0" x2="1">
<stop offset="0" stop-color="white"/>
<stop offset="1" stop-color="black"/>
</linearGradient>
<rect width="100" height="100" fill="url(#fade)"/>
</mask>
</defs>
<image href="photo.jpg" width="100" height="100" clip-path="url(#circle-clip)"/>
<rect width="100" height="100" fill="blue" mask="url(#fade-mask)"/>
SVG Animation
CSS Animation
<style>
.spin { animation: rotate 2s linear infinite; transform-origin: center; }
@keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
.pulse { animation: pulse 1s ease-in-out alternate infinite; }
@keyframes pulse { from { opacity: 1; } to { opacity: 0.3; } }
</style>
<circle cx="50" cy="50" r="30" fill="blue" class="spin"/>
<circle cx="100" cy="50" r="20" fill="red" class="pulse"/>
SMIL Animation (SVG-native)
<circle cx="50" cy="50" r="30" fill="blue">
<!-- Animate the cx attribute from 50 to 150 and back -->
<animate attributeName="cx" from="50" to="150" dur="2s"
repeatCount="indefinite" calcMode="linear"/>
<!-- Animate fill color -->
<animate attributeName="fill" values="blue;red;blue" dur="3s"
repeatCount="indefinite"/>
</circle>
<!-- Motion path animation -->
<circle r="10" fill="red">
<animateMotion dur="4s" repeatCount="indefinite">
<mpath href="#my-path"/>
</animateMotion>
</circle>
SVG Optimization
SVG files from design tools (Figma, Illustrator) contain unnecessary metadata. SVGO is the standard optimization tool:
# Install
npm install -g svgo
# Optimize (default settings)
svgo input.svg -o output.svg
# Show size reduction
svgo --pretty input.svg
# Optimize all SVGs in directory
svgo -r -f ./icons/
# Custom config
svgo --config svgo.config.js input.svg
Typical SVGO reductions: 20–80% file size, depending on how much editor metadata was embedded.
Converting SVG
SVG to PNG/JPEG (Rasterizing)
# Inkscape (best quality, respects SVG features)
inkscape --export-type=png --export-width=512 input.svg -o output.png
# ImageMagick
magick -background none -resize 512x512 input.svg output.png
# rsvg-convert (librsvg, fast)
rsvg-convert -w 512 -h 512 input.svg -o output.png
# Puppeteer (headless Chrome, full CSS/filter support)
node -e "
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({width: 512, height: 512});
await page.goto('file:///path/to/input.svg');
await page.screenshot({path: 'output.png', omitBackground: true});
await browser.close();
})();
"
SVG to PDF
# Inkscape
inkscape --export-type=pdf input.svg -o output.pdf
# Cairo (rsvg-convert)
rsvg-convert -f pdf input.svg -o output.pdf
SVG to ICO (favicon)
# Convert to multiple sizes, then bundle
magick -background none input.svg -resize 16x16 16.png
magick -background none input.svg -resize 32x32 32.png
magick -background none input.svg -resize 48x48 48.png
magick 16.png 32.png 48.png favicon.ico
SVG vs Canvas vs WebGL
| Feature | SVG | Canvas (2D) | WebGL |
|---|---|---|---|
| Type | Retained mode (DOM) | Immediate mode | GPU-accelerated immediate |
| Scalability | Infinite (vector) | Raster (fixed resolution) | Raster (fixed buffer) |
| DOM interaction | Yes (each element is DOM node) | No | No |
| CSS/JS animation | Yes | Manual | Manual |
| Accessibility | Yes (ARIA, text readable) | No | No |
| Search engine indexing | Yes | No | No |
| Best for | Icons, illustrations, charts | Games, image processing | 3D, complex visual effects |
| Performance (10k+ elements) | Slow (DOM overhead) | Fast | Very fast |
| Print quality | Perfect | Resolution-dependent | Resolution-dependent |
SVG is the right choice when elements need to be interactive, accessible, or searchable, and when the number of elements stays manageable (under ~1000 for smooth interaction). For performance-critical rendering of thousands of elements, Canvas is superior.
SVG's text-based nature — making it versionable, diffable, and programmatically generatable — combined with its infinite scalability makes it the ideal format for all vector graphics work in the modern web stack.
Related conversions
Most teams that read this guide convert images in one of these directions: