Skip to content

Animated SVGs

Replace animated GIFs with SVG files that use CSS keyframe animations and custom properties for parametric control. The result is resolution-independent, smaller, and fully customizable.

When to use

Replace animated GIFs with SVG wherever you need resolution-independent, customizable animations — loading spinners, progress indicators, decorative loops, or any graphic that benefits from parametric control over timing, color, and size.

The pattern

Using an animated SVG in HTML

Point an <img> tag at the SVG file. The embedded CSS animations run automatically:

html
<img src="./spinner.svg" alt="Loading…" />

Or inline the SVG directly for currentColor inheritance and styling access:

html
<div style="color: #333;">
  <svg xmlns="http://www.w3.org/2000/svg" ...>
    <!-- SVG content -->
  </svg>
</div>

Implementing a spinner

Embed a <style> block inside the SVG. Define a @keyframes animation and apply it to elements using CSS custom properties for parametric values like rotation offset and delay:

xml
<svg
  xmlns="http://www.w3.org/2000/svg"
  width="48px"
  height="48px"
  viewBox="0 0 100 100"
  preserveAspectRatio="xMidYMid"
>
  <style>
    @keyframes pulse {
      0% { opacity: 1; }
      100% { opacity: 0; }
    }
    
    rect {
      width: 6px;
      height: 12px;
      x: 48px;
      y: 24px;
      transform: rotate(calc(var(--n) * 360deg / 12));
      transform-origin: center center;
      rx: 3px;
      ry: 3px;
      fill: currentColor;
      animation: pulse 1s infinite ease-out;
      animation-delay: calc(-1s * var(--n) / 12);
    }
  </style>
  <rect style="--n: 1"/>
  <rect style="--n: 2"/>
  <rect style="--n: 3"/>
  <rect style="--n: 4"/>
  <rect style="--n: 5"/>
  <rect style="--n: 6"/>
  <rect style="--n: 7"/>
  <rect style="--n: 8"/>
  <rect style="--n: 9"/>
  <rect style="--n: 10"/>
  <rect style="--n: 11"/>
  <rect style="--n: 12"/>
</svg>

How the spinner works

  1. @keyframes pulse fades each rectangle from fully opaque to transparent.
  2. --n custom property on each <rect> sets its position around the circle using rotate(calc(var(--n) * 360deg / 12)).
  3. animation-delay: calc(-1s * var(--n) / 12) offsets each bar's animation so they pulse in sequence, creating the spinning effect.
  4. fill: currentColor inherits the text color from the embedding context, making the spinner adapt to light and dark themes.

Implementing a pulse ring

Use calc() with custom properties to control the number of elements and their distribution:

xml
<svg
  xmlns="http://www.w3.org/2000/svg"
  width="64px"
  height="64px"
  viewBox="0 0 100 100"
>
  <style>
    @keyframes fade {
      0%, 100% { opacity: 0.2; }
      50% { opacity: 1; }
    }
    
    circle.dot {
      cx: 50;
      cy: 15;
      r: 4;
      fill: currentColor;
      transform: rotate(calc(var(--i) * 360deg / 8));
      transform-origin: 50px 50px;
      animation: fade 1.2s infinite ease-in-out;
      animation-delay: calc(var(--i) * 1.2s / 8);
    }
  </style>
  <circle class="dot" style="--i: 0"/>
  <circle class="dot" style="--i: 1"/>
  <circle class="dot" style="--i: 2"/>
  <circle class="dot" style="--i: 3"/>
  <circle class="dot" style="--i: 4"/>
  <circle class="dot" style="--i: 5"/>
  <circle class="dot" style="--i: 6"/>
  <circle class="dot" style="--i: 7"/>
</svg>

Implementing a breathing icon

This example uses a single-element animation with no custom properties:

xml
<svg
  xmlns="http://www.w3.org/2000/svg"
  width="32px"
  height="32px"
  viewBox="0 0 100 100"
>
  <style>
    @keyframes breathe {
      0%, 100% { r: 20; opacity: 0.6; }
      50% { r: 35; opacity: 1; }
    }
    
    circle {
      cx: 50;
      cy: 50;
      fill: currentColor;
      animation: breathe 2s infinite ease-in-out;
    }
  </style>
  <circle />
</svg>

Trade-offs

ApproachProsCons
Animated SVGCrisp, tiny, themeableNo raster effects
Animated GIFUniversal supportLarge, fixed resolution
CSS on HTMLFull DOM accessNot portable
Lottie / JSComplex animationsRuntime dependency

Prefer animated SVGs for simple looping UI animations like spinners, pulsing indicators, and icon effects. Use Lottie or a JS library only when the animation requires complex multi-stage choreography or imported motion design.