← Back to DevBytes

HTML SVG: Complete Guide

What is SVG?

SVG, which stands for Scalable Vector Graphics, is an XML-based markup language for describing two-dimensional vector graphics. Unlike raster image formats like JPEG, PNG, or GIF (which are made up of a fixed grid of pixels), SVG images are composed of mathematical descriptions of shapes, paths, text, and gradients. This fundamental difference brings powerful capabilities to web developers and designers alike.

When you include an SVG in your HTML document, you are not embedding a static picture. You are embedding a living, XML-based description that the browser renders dynamically. Every circle, every curve, every text element is a DOM node that can be styled with CSS, animated with JavaScript, or manipulated just like any other HTML element.

Here is the simplest possible SVG β€” a red circle β€” embedded directly in HTML:

<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
  <circle cx="50" cy="50" r="40" fill="crimson" />
</svg>

The xmlns attribute is required when using inline SVG in HTML5 documents (though modern browsers may be forgiving if you omit it, it is best practice to always include it). The coordinate system starts from the top-left corner, and elements are drawn in the order they appear β€” later elements appear on top of earlier ones, much like the z-index in CSS.

Why SVG Matters

πŸš€ Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

SVG solves several critical problems that raster images simply cannot address, making it an essential tool in modern web development:

Getting Started with SVG in HTML

There are several ways to include SVG in an HTML document. Each method has different implications for styling, scripting, and caching.

Method 1: Inline SVG (Recommended for Interactivity)

Inline SVG places the entire SVG markup directly inside your HTML. This gives you the most control: you can style it with your page's CSS, manipulate it with JavaScript, and it incurs no extra HTTP request.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Inline SVG Example</title>
  <style>
    .hover-circle:hover {
      fill: gold;
      cursor: pointer;
      transition: fill 0.3s;
    }
  </style>
</head>
<body>
  <svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
    <circle cx="100" cy="100" r="60" fill="steelblue" class="hover-circle" />
    <text x="100" y="105" text-anchor="middle" fill="white" font-size="18">SVG</text>
  </svg>
</body>
</html>

Notice how the CSS class .hover-circle targets the SVG <circle> element directly. The browser treats it as a first-class citizen in the DOM. The fill property is an SVG presentation attribute that maps to a CSS property, so CSS transitions work beautifully.

Method 2: SVG via <img> Tag

Use the <img> element when your SVG is a static asset that doesn't need dynamic manipulation β€” like a logo or illustration. This method is simple and benefits from browser caching.

<img src="logo.svg" alt="Company logo" width="150" height="50">

Important limitation: SVGs loaded via <img> or as a CSS background-image cannot be manipulated by JavaScript and cannot inherit styles from the parent page's CSS. The SVG must contain all its styling internally.

Method 3: SVG via <object> or <iframe>

The <object> element provides a middle ground: the SVG remains an external file (cacheable), yet you can access its internal DOM from JavaScript (subject to same-origin restrictions).

<object data="interactive-map.svg" type="image/svg+xml" width="800" height="600">
  Your browser does not support SVGs.
</object>

The fallback content inside the <object> tag displays if the browser cannot render the SVG. This pattern is useful for progressive enhancement.

Common SVG Shapes

SVG provides a set of basic shape elements that cover most needs. Here is a comprehensive example demonstrating all fundamental shapes:

<svg width="500" height="300" xmlns="http://www.w3.org/2000/svg">
  <!-- Rectangle -->
  <rect x="10" y="10" width="100" height="60" fill="tomato" rx="8" />

  <!-- Circle -->
  <circle cx="160" cy="40" r="30" fill="seagreen" />

  <!-- Ellipse -->
  <ellipse cx="250" cy="40" rx="40" ry="20" fill="goldenrod" />

  <!-- Line -->
  <line x1="320" y1="20" x2="400" y2="60" stroke="navy" stroke-width="3" />

  <!-- Polygon (triangle) -->
  <polygon points="430,10 470,60 390,60" fill="orchid" />

  <!-- Polyline (open shape) -->
  <polyline points="10,100 50,130 90,100 130,140" fill="none" stroke="coral" stroke-width="3" />

  <!-- Path (curved shape) -->
  <path d="M 200 100 Q 250 60 300 100 T 400 100" fill="none" stroke="teal" stroke-width="4" />

  <!-- Text -->
  <text x="250" y="180" text-anchor="middle" font-family="Arial" font-size="24" fill="darkslateblue">
    SVG Shapes Demo
  </text>
</svg>

Each shape has its own set of specific attributes. The <path> element is the most powerful β€” its d attribute uses a compact command language to describe complex curves, arcs, and combined shapes. Mastering path commands (M, L, C, Q, A, Z) unlocks virtually any vector illustration.

The <path> Element in Detail

Paths are the backbone of SVG. Let's break down a more sophisticated path example:

<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
  <path
    d="M 30 100 
       C 30 30, 170 30, 170 100 
       S 30 170, 30 100 
       Z"
    fill="mediumvioletred"
    stroke="darkred"
    stroke-width="4"
  />
</svg>

Breaking down the d attribute:

Gradients and Patterns

SVG supports linear and radial gradients defined in a <defs> section. These gradients can be referenced by ID, promoting reusability.

<svg width="400" height="200" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="fadeGradient" x1="0%" y1="0%" x2="100%" y2="100%">
      <stop offset="0%" stop-color="royalblue" stop-opacity="1" />
      <stop offset="50%" stop-color="violet" stop-opacity="0.7" />
      <stop offset="100%" stop-color="crimson" stop-opacity="1" />
    </linearGradient>

    <radialGradient id="sunGradient" cx="50%" cy="50%" r="50%">
      <stop offset="0%" stop-color="gold" />
      <stop offset="100%" stop-color="orangered" />
    </radialGradient>
  </defs>

  <rect x="10" y="10" width="180" height="180" fill="url(#fadeGradient)" rx="10" />
  <circle cx="290" cy="100" r="80" fill="url(#sunGradient)" />
</svg>

The <defs> element stores definitions without rendering them. Anything inside <defs> must be explicitly referenced (via url(#id), <use>, or other means) to appear on screen. This pattern is essential for keeping your SVG code DRY.

Strokes, Dashes, and Advanced Styling

Beyond simple fill and stroke, SVG offers fine-grained control over how outlines are drawn:

<svg width="400" height="150" xmlns="http://www.w3.org/2000/svg">
  <line
    x1="30" y1="30" x2="370" y2="30"
    stroke="darkgreen"
    stroke-width="8"
    stroke-linecap="round"
    stroke-dasharray="20, 10, 5, 10"
  />

  <circle
    cx="200" cy="90" r="40"
    fill="none"
    stroke="darkorange"
    stroke-width="6"
    stroke-dasharray="8, 4"
    stroke-linecap="butt"
  />
</svg>

The stroke-dasharray attribute defines a pattern of dash lengths and gap lengths. The values 20, 10, 5, 10 mean: draw a dash of length 20, a gap of 10, a dash of 5, a gap of 10, then repeat. stroke-linecap controls the shape at the ends of strokes: round, butt, or square.

Responsive SVG

Making SVG scale properly requires understanding the viewBox attribute and the interplay with width and height. The viewBox defines the internal coordinate system, while width and height define the element's size in the page layout.

<svg viewBox="0 0 200 100" width="100%" height="auto" xmlns="http://www.w3.org/2000/svg">
  <rect x="10" y="10" width="180" height="80" fill="teal" rx="10" />
  <text x="100" y="55" text-anchor="middle" fill="white" font-size="16">
    Responsive SVG
  </text>
</svg>

By setting width="100%" and omitting a fixed height (or using height="auto"), the SVG will scale to fill its container while maintaining the aspect ratio defined by the viewBox. The viewBox values are min-x min-y width height β€” here, the internal coordinate space runs from (0, 0) to (200, 100).

For truly responsive icons, a common pattern is:

<svg viewBox="0 0 24 24" width="1em" height="1em" xmlns="http://www.w3.org/2000/svg">
  <path d="M12 2L2 22h20L12 2z" fill="currentColor" />
</svg>

Using width="1em" height="1em" makes the icon scale proportionally to the surrounding text size. The fill="currentColor" magic word inherits the current CSS text color, so the icon automatically matches the surrounding typography.

SVG Sprites and the <use> Element

SVG sprites allow you to define symbols once and reuse them multiple times β€” similar to how CSS sprites worked, but far more elegant:

<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  <defs>
    <symbol id="icon-home" viewBox="0 0 24 24">
      <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
    </symbol>
    <symbol id="icon-search" viewBox="0 0 24 24">
      <path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.59 19l-5.09-5zm-6 0A4.5 4.5 0 1 1 14 9.5 4.5 4.5 0 0 1 9.5 14z" />
    </symbol>
  </defs>
</svg>

<!-- Usage elsewhere in the HTML -->
<svg width="48" height="48">
  <use href="#icon-home" fill="darkblue" />
</svg>

<svg width="48" height="48">
  <use href="#icon-search" fill="darkblue" />
</svg>

Key points about sprites:

Clipping and Masking

SVG allows you to define clipping paths and masks to control which parts of elements are visible:

<svg width="300" height="200" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <clipPath id="circleClip">
      <circle cx="150" cy="100" r="60" />
    </clipPath>
  </defs>

  <rect x="80" y="40" width="140" height="120" fill="mediumslateblue" />
  <image 
    href="https://placekitten.com/200/200" 
    x="80" y="40" width="140" height="120"
    clip-path="url(#circleClip)"
  />
</svg>

A <clipPath> restricts rendering to the area defined by its contents β€” anything outside the clipping region is completely hidden. Masks, by contrast, use luminance or alpha values to control opacity per-pixel, enabling soft-edged reveals.

SVG Filters

SVG filters provide powerful graphical effects like blur, shadow, and color manipulation, all declaratively:

<svg width="300" height="200" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <filter id="dropShadow" x="-20%" y="-20%" width="140%" height="140%">
      <feDropShadow dx="4" dy="6" stdDeviation="3" flood-color="rgba(0,0,0,0.4)" />
    </filter>

    <filter id="blurEffect">
      <feGaussianBlur stdDeviation="5" />
    </filter>
  </defs>

  <rect x="30" y="30" width="100" height="80" fill="lightseagreen" filter="url(#dropShadow)" />
  <circle cx="210" cy="70" r="40" fill="hotpink" filter="url(#blurEffect)" opacity="0.7" />
</svg>

Filter effects like <feDropShadow>, <feGaussianBlur>, <feColorMatrix>, and <feMerge> can be combined in chains to create sophisticated visual results β€” all rendered natively by the browser without any external image processing.

SVG Animation with <animate>

SVG includes a declarative animation engine that works without any JavaScript. While CSS animations are now more common, native SVG animations remain useful for certain scenarios:

<svg width="300" height="100" xmlns="http://www.w3.org/2000/svg">
  <rect x="10" y="10" width="40" height="40" fill="coral">
    <animate
      attributeName="x"
      values="10; 250; 10"
      dur="3s"
      repeatCount="indefinite"
    />
    <animate
      attributeName="fill"
      values="coral; seagreen; coral"
      dur="3s"
      repeatCount="indefinite"
    />
  </rect>

  <circle cx="150" cy="50" r="20" fill="royalblue">
    <animate
      attributeName="r"
      values="20; 35; 20"
      dur="2s"
      repeatCount="indefinite"
    />
    <animate
      attributeName="opacity"
      values="1; 0.4; 1"
      dur="2s"
      repeatCount="indefinite"
    />
  </circle>
</svg>

The <animate> element changes an attribute of its parent element over time. The values attribute defines keyframes (semicolon-separated), dur sets the duration, and repeatCount="indefinite" loops the animation forever. Multiple <animate> elements can animate different properties simultaneously.

Interacting with SVG via JavaScript

Because inline SVG becomes part of the DOM, you can attach event handlers, query elements, and dynamically modify attributes just like any HTML element:

<svg id="interactive-svg" width="400" height="300" xmlns="http://www.w3.org/2000/svg">
  <rect id="click-rect" x="100" y="100" width="200" height="100" fill="indigo" rx="10" />
  <text id="click-counter" x="200" y="155" text-anchor="middle" fill="white" font-size="20">
    Clicks: 0
  </text>
</svg>

<script>
  (() => {
    const rect = document.getElementById('click-rect');
    const counter = document.getElementById('click-counter');
    let clicks = 0;

    rect.addEventListener('click', () => {
      clicks += 1;
      counter.textContent = `Clicks: ${clicks}`;
      rect.setAttribute('fill', clicks % 2 === 0 ? 'indigo' : 'darkorange');
    });

    rect.addEventListener('mouseenter', () => {
      rect.style.cursor = 'pointer';
      rect.style.opacity = '0.8';
    });

    rect.addEventListener('mouseleave', () => {
      rect.style.opacity = '1';
    });
  })();
</script>

This example demonstrates DOM queries (getElementById), event listeners (click, mouseenter, mouseleave), text content manipulation, attribute changes, and inline style modifications β€” all targeting SVG nodes directly. The integration between SVG and the HTML DOM is seamless.

Creating SVG Charts and Data Visualizations

SVG excels at data visualization. Here is a simple bar chart generated programmatically:

<svg id="chart" width="400" height="250" xmlns="http://www.w3.org/2000/svg">
  <!-- Axes rendered statically -->
  <line x1="40" y1="20" x2="40" y2="220" stroke="black" stroke-width="2" />
  <line x1="40" y1="220" x2="380" y2="220" stroke="black" stroke-width="2" />
</svg>

<script>
  (() => {
    const data = [
      { label: 'Jan', value: 45 },
      { label: 'Feb', value: 72 },
      { label: 'Mar', value: 38 },
      { label: 'Apr', value: 91 },
      { label: 'May', value: 64 },
      { label: 'Jun', value: 53 },
    ];

    const chart = document.getElementById('chart');
    const chartHeight = 200; // from y=20 to y=220
    const maxValue = Math.max(...data.map(d => d.value));
    const barWidth = 40;
    const gap = 15;
    const startX = 60;

    data.forEach((item, i) => {
      const barHeight = (item.value / maxValue) * chartHeight;
      const x = startX + i * (barWidth + gap);
      const y = 220 - barHeight;

      // Create bar
      const bar = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
      bar.setAttribute('x', x);
      bar.setAttribute('y', y);
      bar.setAttribute('width', barWidth);
      bar.setAttribute('height', barHeight);
      bar.setAttribute('fill', `hsl(${i * 40}, 70%, 55%)`);
      bar.setAttribute('rx', '4');
      chart.appendChild(bar);

      // Create label
      const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
      label.setAttribute('x', x + barWidth / 2);
      label.setAttribute('y', 240);
      label.setAttribute('text-anchor', 'middle');
      label.setAttribute('fill', 'black');
      label.setAttribute('font-size', '12');
      label.textContent = item.label;
      chart.appendChild(label);

      // Create value text atop bar
      const valueText = document.createElementNS('http://www.w3.org/2000/svg', 'text');
      valueText.setAttribute('x', x + barWidth / 2);
      valueText.setAttribute('y', y - 8);
      valueText.setAttribute('text-anchor', 'middle');
      valueText.setAttribute('fill', 'darkslategray');
      valueText.setAttribute('font-size', '11');
      valueText.textContent = item.value;
      chart.appendChild(valueText);
    });
  })();
</script>

Note the use of document.createElementNS('http://www.w3.org/2000/svg', ...) β€” when creating SVG elements via JavaScript, you must use the namespace-aware method, because SVG elements belong to a different XML namespace than HTML elements.

Accessibility in SVG

Making SVG accessible involves providing semantic metadata and ensuring screen readers can convey the content meaningfully:

<svg viewBox="0 0 200 100" width="400" height="200" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="title desc">
  <title id="title">Quarterly Revenue Growth</title>
  <desc id="desc">A bar chart showing revenue increasing from 50K in Q1 to 95K in Q4</desc>

  <rect x="20" y="80" width="30" height="20" fill="steelblue">
    <title>Q1: 50,000</title>
  </rect>
  <rect x="60" y="60" width="30" height="40" fill="steelblue">
    <title>Q2: 65,000</title>
  </rect>
  <rect x="100" y="40" width="30" height="60" fill="steelblue">
    <title>Q3: 80,000</title>
  </rect>
  <rect x="140" y="20" width="30" height="80" fill="steelblue">
    <title>Q4: 95,000</title>
  </rect>
</svg>

Best practices for accessible SVG:

Best Practices

Conclusion

SVG is far

πŸš€ Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started β€” $23.99/mo
← Back to all articles