Skip to content
SVG Weave

How to animate an SVG in 2026: CSS, SMIL, JavaScript or Lottie

SVG Weave team
Published

Four ways to animate an SVG, compared honestly: CSS, SMIL, JavaScript and Lottie. Working code, browser support and when an AI tool is worth using.

To animate an SVG, put CSS @keyframes in a <style> block inside the file and attach them with the animation property. It needs no JavaScript and no library, and it keeps working when the SVG is loaded through an <img> tag. Reach for JavaScript when you need scroll or click control, and for Lottie when a motion designer hands you an After Effects file.

Which method should you pick?

  • Icon, loader or logo on a website: CSS @keyframes.
  • Must work as an <img> or a CSS background: CSS or SMIL. Not JavaScript.
  • Many coordinated elements, or scroll and click control: JavaScript (GSAP or the Web Animations API).
  • Motion designed in After Effects, shipped to web and mobile: Lottie.
  • You can describe the motion but do not want to write keyframes: an AI tool, then review the output.

The four approaches compared

CSS @keyframesSMIL (<animate>)JavaScript (GSAP, WAAPI)Lottie
Browser supportBaseline for all modern browsersAll current browsers, not IEAll modern browsersNeeds a player library; runs wherever the player does
Works in <img src="x.svg">Yes, from an inline <style> blockYesNo: scripts are disabled in image contextsNot as an image; needs a player
File size impactA few hundred bytes to a few KB of CSSSmall, but verbose markupYour code plus the libraryA JSON (or dotLottie) file plus the player
InteractivityHover and focus via CSS onlyLimited (begin events)FullVia the player API
ToolingAny editor; browser DevTools animation panelThinMatureAfter Effects plus Bodymovin, or Lottie editors
Path morphingCSS d property works in Chrome and Firefox, not SafariYesYes (with a plugin in GSAP)Yes
Best forIcons, loaders, logo motionPure-markup animation, path motionSequenced and interactive scenesDesigner-made motion across platforms

Notes on the table:

  • SMIL had a rocky reputation after Chrome once announced plans to deprecate it. The caniuse data shows full support from Chrome 5, Firefox 4, Safari 6 and Edge 79, so it is safe to use. Tooling and community knowledge are thinner than for CSS, which is the practical reason to prefer CSS.
  • GSAP is free for all users, including its plugins, as of this writing. That removes the old licensing argument against it. Weight and the <img> limitation remain.
  • Lottie files are JSON played by lottie-web or a dotLottie runtime. They are not SVG files, so you cannot paste one into an <img> or edit it as SVG.

CSS keyframes: the default choice

A complete, self-contained animated SVG. Save it as star.svg and open it in a browser.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="200" height="200">
  <style>
    #star {
      transform-box: fill-box;
      transform-origin: center;
      animation: pulse 2s ease-in-out infinite;
    }
    @keyframes pulse {
      0%, 100% { transform: scale(1); }
      50%      { transform: scale(1.25); }
    }
    @media (prefers-reduced-motion: reduce) {
      #star { animation: none; }
    }
  </style>
  <polygon id="star" fill="#f5b301"
    points="50,10 61,38 91,38 67,56 76,86 50,68 24,86 33,56 9,38 39,38" />
</svg>

Three things are doing real work here:

  1. The id gives the CSS something to select. Animation targets live in the same file, so the SVG stays portable.
  2. transform-box: fill-box and transform-origin: center make the star scale around its own middle (more on this below).
  3. The prefers-reduced-motion block turns the animation off for people who asked their OS for less motion. Always include it. The MDN reference explains why: large scaling and panning can cause discomfort for people with vestibular disorders.

If you want the full property-by-property tour, see our CSS @keyframes for SVG guide.

The line-draw effect with stroke-dashoffset

The classic "signature being drawn" effect works by making the stroke one big dash, then sliding that dash into view.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 60" width="360" height="180">
  <style>
    .draw {
      fill: none;
      stroke: #1d4ed8;
      stroke-width: 4;
      stroke-dasharray: 100;
      stroke-dashoffset: 100;
      animation: draw 2.5s ease-out forwards;
    }
    @keyframes draw {
      to { stroke-dashoffset: 0; }
    }
    @media (prefers-reduced-motion: reduce) {
      .draw { animation: none; stroke-dashoffset: 0; }
    }
  </style>
  <path class="draw" pathLength="100" d="M10 40 C 30 0, 50 0, 60 30 S 90 60, 110 20" />
</svg>

How it works:

  • stroke-dasharray: 100 makes a pattern of a 100-unit dash followed by a 100-unit gap.
  • stroke-dashoffset: 100 shifts the pattern so the path starts inside the gap. Nothing is visible.
  • Animating the offset to 0 slides the dash over the path, revealing it from the start.
  • pathLength="100" tells the browser to treat the path as 100 units long no matter its real length, so the numbers above work for any path without measuring it in JavaScript. stroke-dashoffset is Baseline widely available.
  • The reduced-motion branch shows the finished line instead of hiding it. Setting only animation: none would leave the path invisible.

Gotcha: with stroke-linecap: round, a small dot can show at the start of the path before the animation begins. Keep the default butt caps for this effect, or start the offset a little past the length.

The transform-origin and transform-box gotcha

This catches almost everyone. In HTML, transform: rotate() pivots around the element's center. In SVG, it does not, by default.

The fix is two lines on the animated element:

.gear {
  transform-box: fill-box;   /* use the shape's own bounding box */
  transform-origin: center;  /* pivot on its middle */
  animation: spin 6s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

transform-box has been Baseline since January 2020. If you want a pivot that is not the center, such as the hinge of a door, keep fill-box and use a percentage like transform-origin: 0% 50%.

SMIL: when markup-only animation is useful

SMIL animates attributes directly, with no stylesheet:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 60" width="400" height="120">
  <circle cx="20" cy="30" r="12" fill="#e11d48">
    <animate attributeName="cx" from="20" to="180" dur="2s"
             repeatCount="indefinite" />
  </circle>
</svg>

SMIL's useful edge is <animateMotion>, which moves an element along a path, and attribute-level animation like cx or points. It is documented in MDN's SMIL guide. For everything else, CSS is easier to read and debug.

JavaScript: when CSS stops being readable

If you are coordinating a dozen elements on a timeline, reversing on scroll, or reacting to clicks, write it in JavaScript. The built-in Web Animations API needs no library:

<svg viewBox="0 0 100 100" width="160" height="160">
  <circle id="dot" cx="50" cy="50" r="12" fill="#0ea5e9" />
</svg>
<script>
  document.getElementById('dot').animate(
    [{ transform: 'translateX(-30px)' }, { transform: 'translateX(30px)' }],
    { duration: 1200, iterations: Infinity, direction: 'alternate', easing: 'ease-in-out' }
  );
</script>

This must be inline SVG in an HTML page. The same script inside an SVG loaded with <img> never runs.

Where an AI tool like SVG Weave fits

Writing keyframes by hand is fine for one icon. It gets tedious for a 30-element illustration where each part needs its own timing and stagger. That is the gap tools like SVG Weave target: you give it an SVG (uploaded, pasted, or generated from text or an image), describe the motion in a sentence, and it returns the same SVG with CSS @keyframes in a style block. No JavaScript, so the result follows the same rules as the CSS section above, including <img> compatibility.

What it is good at:

  • Staggered, organic motion across many shapes that would take an hour to hand-tune.
  • Starting from an image: Image to SVG Converter vectorizes a flat logo or icon first, then you animate it.
  • Iterating by chaining prompts instead of editing keyframes.

What it does not do, and where you should use something else:

  • Interactivity. The output is looping CSS, not click or scroll handlers.
  • Exact brand motion specs. If your motion designer specified easing curves and timings, write the CSS or use Lottie.
  • Lottie export. The output is SVG, not Lottie JSON.
  • Guaranteed results. Generated animation varies, so preview it and edit the code. Layered or ungrouped SVGs animate worse than clean ones.

It is paid and credit-based, with no free tier: packs start at $5 for 150 credits, and an average animation render costs about 18 credits. If you only need one simple animation, the CSS above costs nothing.

An example of the kind of CSS-keyframes animation the generator produces: floating body, flickering flames. The keyframes in this demo were hand-written to illustrate the output format.

Pitfalls checklist

  • Nothing happens in <img>. Check you used CSS or SMIL, not a script. See MDN's SVG as an image restrictions: no scripts and no external resources, so inline everything.
  • Shape rotates around the corner. Add transform-box: fill-box; transform-origin: center.
  • Stroke animation is jittery or too long. Use pathLength so dash values are predictable.
  • Animation runs forever on a page that should be calm. Add the reduced-motion block, and consider fewer iterations for non-essential decoration.
  • Email. Many email clients do not render SVG animation at all. Use a GIF or a static fallback there. See animated SVG vs Lottie vs GIF vs video.

FAQ

What is the easiest way to animate an SVG without code?

Describe the motion to an AI tool that writes the animation for you, or use a timeline editor where you set keyframes visually. SVG Weave is an example of the first approach, and SVGator is a well-known example of the second. Either way, review the exported SVG in the browsers you target.

Can I animate an SVG with CSS only?

Yes. Add @keyframes in a <style> block inside the SVG and apply them with the animation property. No JavaScript is needed. Add transform-box: fill-box and transform-origin: center if you rotate or scale.

Does an animated SVG work in an img tag?

CSS and SMIL animations defined inside the SVG can play when it is loaded through <img>. JavaScript cannot, because scripts and external resources are disabled in image contexts. Test your target browsers, since older bugs have affected <img> animation in some engines.

CSS vs SMIL vs JavaScript: which should I use?

Use CSS for icons, loaders and logo motion. Use JavaScript for sequenced timelines, scroll-driven or interactive animation. Use SMIL when you want animation that lives entirely in SVG markup, or you need <animateMotion>, and CSS cannot do the job.

Should I use Lottie or animated SVG?

Lottie suits complex After Effects motion and cross-platform apps, but needs a player library and the file is JSON rather than SVG. Animated SVG with CSS needs no runtime and suits web icons, loaders and logos.

Can AI animate an SVG I already have?

Yes. SVG Weave accepts an existing SVG and a prompt describing the motion, and returns CSS keyframes. Quality depends on how cleanly the SVG is grouped, so name or group the parts you want to move, and always preview the result.

Try the SVG Animation Generator

Describe the motion in plain English and get an animated SVG with embedded CSS @keyframes. Works on SVGs you upload, paste or generate. No JavaScript.

Related tools

  • SVG Animation Generator

    Describe the motion in plain English and get an animated SVG with embedded CSS @keyframes. Works on SVGs you upload, paste or generate. No JavaScript.

  • CSS @keyframes Generator

    Generate CSS @keyframes for SVG elements from a plain-English description: ready-to-paste keyframes with timing, easing and staggered delays, in the SVG.