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 @keyframes | SMIL (<animate>) | JavaScript (GSAP, WAAPI) | Lottie | |
|---|---|---|---|---|
| Browser support | Baseline for all modern browsers | All current browsers, not IE | All modern browsers | Needs a player library; runs wherever the player does |
Works in <img src="x.svg"> | Yes, from an inline <style> block | Yes | No: scripts are disabled in image contexts | Not as an image; needs a player |
| File size impact | A few hundred bytes to a few KB of CSS | Small, but verbose markup | Your code plus the library | A JSON (or dotLottie) file plus the player |
| Interactivity | Hover and focus via CSS only | Limited (begin events) | Full | Via the player API |
| Tooling | Any editor; browser DevTools animation panel | Thin | Mature | After Effects plus Bodymovin, or Lottie editors |
| Path morphing | CSS d property works in Chrome and Firefox, not Safari | Yes | Yes (with a plugin in GSAP) | Yes |
| Best for | Icons, loaders, logo motion | Pure-markup animation, path motion | Sequenced and interactive scenes | Designer-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:
- The
idgives the CSS something to select. Animation targets live in the same file, so the SVG stays portable. transform-box: fill-boxandtransform-origin: centermake the star scale around its own middle (more on this below).- The
prefers-reduced-motionblock 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: 100makes a pattern of a 100-unit dash followed by a 100-unit gap.stroke-dashoffset: 100shifts the pattern so the path starts inside the gap. Nothing is visible.- Animating the offset to
0slides 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-dashoffsetis Baseline widely available.- The reduced-motion branch shows the finished line instead of hiding it. Setting only
animation: nonewould 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.
- SVG elements default to
transform-box: view-box, so the origin is measured against the whole SVG canvas, not the shape. - Without a
transform-origin, the origin for SVG elements is the top-left of that box. Your gear swings around the corner of the canvas.
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.
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
pathLengthso 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.