ICON OOP
Icons & Logos
Guide

How to animate SVG icons

A small amount of motion makes an interface feel alive: a spinner that turns, a bell that rings, a checkmark that draws itself in. SVG is built for this. Here are the three ways to animate an icon, when to reach for each, and how to keep the motion tasteful and accessible.

HomeGuidesAnimate SVG icons

An SVG is drawing instructions rather than a flat picture, which means every line, curve and fill inside it is a real element you can target and move. That is what makes SVG such a natural fit for animation. You are not nudging a static image around; you are animating the shapes themselves. A stroke can appear to draw, a group can rotate, a colour can shift, all while the icon stays perfectly sharp.

This guide covers the practical toolkit. We start with CSS, which handles the majority of what you will ever need, then move to the line-drawing effect that SVG is famous for, then to SMIL for animation that lives inside the file, and finally to when a JavaScript library is actually worth the weight. Throughout, the aim is motion that adds meaning rather than motion for its own sake. If you are new to the format, the explainer on what an SVG file is and the guide to using SVG icons are useful groundwork.

The short version

For most icons, inline the SVG and animate it with CSS: a transition for hover states, a @keyframes loop for spinners. Animate transform and opacity, not layout properties, so it stays smooth. Use stroke-dasharray for the draw-on effect, reach for SMIL only when the motion must travel inside the file, and always wrap it in a prefers-reduced-motion check.

Three ways to animate an SVG

There are three established approaches, and the right one depends mostly on where the SVG lives and how self-contained the animation needs to be.

ApproachBest forNote
CSSHover effects, spinners, page-triggered motionNeeds the SVG inline in the HTML so CSS can reach it
SMIL (<animate>)Motion that must travel with the fileWritten inside the SVG; works even as an img
JavaScript libraryComplex, scripted or timeline animationPowerful but adds weight; overkill for simple icons

The single most important requirement is this: your page CSS can only animate an SVG that is inline in the HTML. If the icon is loaded through an <img> tag or a CSS background, your stylesheet cannot see inside it, so CSS animation is off the table and you would need SMIL instead. That one rule decides a lot, so keep it in mind as you read on.

Animate with CSS

CSS is where you should start, because it uses skills you already have and covers the vast majority of icon animation. Inline the SVG, give the part you want to move a class, and animate it like any other element.

Hover with a transition. A transition smoothly interpolates a property when its value changes, which is perfect for hover states. Here a settings icon eases into a quarter turn when you point at it:

.gear { transition: transform .3s ease; transform-origin: center; }
.gear:hover { transform: rotate(90deg); }

Loop with keyframes. For continuous motion like a loading spinner, define a @keyframes sequence and run it forever:

@keyframes spin { to { transform: rotate(360deg); } }
.spinner { transform-origin: center; animation: spin .9s linear infinite; }

That is genuinely all a spinner needs. The same pattern, with different keyframes, gives you a pulsing dot, a bouncing arrow or a shaking bell.

The transform-origin trap

The most common CSS-on-SVG surprise is rotation happening around the wrong point. By default an SVG element rotates around the origin of its coordinate system, the top-left corner, not its own centre, so an icon can swing wildly off to one side. Fix it by setting transform-origin: center on the element, or by adding transform-box: fill-box so the origin is measured against the shape's own box. Set both and rotation behaves the way you expect.

The draw-on effect with stroke-dasharray

If there is one animation people associate with SVG, it is a line drawing itself onto the screen, the trick behind those satisfying animated checkmarks and signatures. It relies on two stroke properties working together, and it only works on stroked paths, not solid fills.

A dashed stroke is controlled by stroke-dasharray (the pattern of dashes and gaps) and stroke-dashoffset (how far the pattern is shifted along the line). The technique is to make a single dash exactly as long as the whole path, then offset it by that same length so the entire stroke sits in the gap and is invisible. Animating the offset back to zero slides the dash into view, so the line appears to draw:

.draw { stroke-dasharray: 100; stroke-dashoffset: 100; animation: draw 1s ease forwards; }
@keyframes draw { to { stroke-dashoffset: 0; } }

The one value you need is the path's length, used for both the dasharray and the starting offset. You can read it exactly in the browser with one line of JavaScript, path.getTotalLength(), then drop that number into your CSS. For a checkmark or a short icon path, a round number a little larger than the visible length is usually close enough to look right. Pair the draw with a fade or a slight scale and you have the confirmation animation seen all over modern interfaces.

Animation inside the file with SMIL

Sometimes you want the animation to belong to the icon itself, so it plays wherever the file goes, even when it is dropped in as an <img> or shared with someone who never sees your CSS. That is what SMIL is for. SMIL animation is written as elements inside the SVG, most often <animate> for a single attribute and <animateTransform> for movement, rotation or scaling.

A self-contained spinner looks like this, with the rotation defined right inside the SVG:

<circle cx="12" cy="12" r="9" fill="none" stroke="currentColor"
  stroke-width="2.4" stroke-linecap="round" stroke-dasharray="42 14">
  <animateTransform attributeName="transform" type="rotate"
    from="0 12 12" to="360 12 12" dur="0.9s" repeatCount="indefinite"/>
</circle>

Notice the rotation centre is written as part of the value, 12 12, so SMIL sidesteps the transform-origin trap that catches CSS. The animated icons in the ICON OOP tool are built exactly this way, which is why they keep moving when you save one and reuse it elsewhere: the motion is baked into the file rather than added by a stylesheet.

You may have heard SMIL was being removed from browsers. That was proposed for Chrome years ago and then reversed, so it is supported across current browsers and safe to use where self-contained animation is genuinely what you need. For animation the page controls, such as a hover or a spinner that only appears while loading, CSS is still the simpler tool.

When to reach for a JavaScript library

CSS and SMIL cover almost everything an icon needs. A JavaScript animation library earns its place only when you step beyond simple, self-contained motion:

  • Scripted timelines and sequencing. When several elements must animate in a precisely choreographed order with fine timing control, a library such as GSAP makes that far easier than hand-written keyframes.
  • Designer-authored animation. Complex illustrated animations exported from design tools are often delivered as Lottie files and played with a small runtime, which is a different job from animating a UI icon.
  • Interaction-driven motion. Animation that must respond continuously to scrolling, dragging or live data usually needs JavaScript to drive it frame by frame.

For a spinner, a hover wiggle or a checkmark, a library is weight you do not need. Reach for one when the animation itself is the feature, not when you are decorating a button. Keeping simple icons on plain CSS also keeps your pages light, which matters more than a fancier animation almost every time.

Micro-interactions worth adding

Good icon animation is quiet. It confirms an action or draws the eye for a moment, then gets out of the way. A few that reliably improve an interface:

  • A spinner while something loads. The clearest possible signal that the app is working and has not frozen.
  • A checkmark that draws on success. A brief, positive confirmation that an action completed, using the stroke-draw effect above.
  • A gentle hover shift on interactive icons. A small rotation, scale or colour change tells people an icon is clickable before they click it.
  • A single bell shake for a new notification. One short movement catches attention without nagging. Repeating it endlessly does the opposite.
  • A smooth toggle between two states. A menu icon easing into a close icon, or a chevron rotating open, makes state changes feel intentional.

The common thread is restraint. Motion that plays once, in response to something the user did, reads as polish. Motion that loops forever for no reason reads as noise and, worse, as a distraction people cannot switch off.

Respect reduced motion

Animation is not free for everyone. For some people, motion on screen can trigger dizziness, nausea or migraine, which is why every major operating system has a setting to reduce motion, and why browsers expose that choice to your CSS. Honouring it is a baseline accessibility expectation, and it is only a few lines.

The prefers-reduced-motion media query lets you scale animation back for anyone who has asked for it, while keeping it for everyone else:

@media (prefers-reduced-motion: reduce) {
  .spinner, .gear, .draw { animation: none; transition: none; }
}

You do not have to remove everything. Often the right move is to keep essential feedback, a spinner still needs to indicate loading, while cutting decorative flourishes and shortening durations. The goal is to respect the request without stripping out information the user relies on. This sits alongside the wider practice in the accessible icons guide, which covers labelling and screen readers; motion preferences are the same idea applied to movement.

Animate the cheap properties

Smooth animation comes down to what you animate, not just how much. Browsers can move and fade elements very cheaply, but changing properties that affect layout forces expensive recalculation on every frame, which is what causes janky, stuttering motion.

  • Prefer transform and opacity. Rotating, scaling, moving and fading are the properties browsers optimise hardest. Almost all icon animation can be expressed with them.
  • Avoid animating layout properties. Changing width, height, top, left or margin makes the browser recompute the page geometry each frame. Use a transform: scale() or translate() instead of resizing or repositioning by layout.
  • Keep it short. Interface animation should feel instant. Most icon transitions land well between 150 and 400 milliseconds. Longer than that starts to feel sluggish.
  • Do not animate dozens of icons at once. A wall of simultaneously moving icons is both a performance cost and a visual mess. Animate the one that matters.

Common mistakes

  • Trying to animate an img or background SVG with CSS. Your stylesheet cannot reach inside a referenced SVG. Inline it, or use SMIL inside the file.
  • Rotation spinning off-centre. The transform-origin trap. Set transform-origin: center and, if needed, transform-box: fill-box.
  • Using the draw-on effect on a filled shape. Line drawing needs a stroked path. A solid fill has no stroke to reveal, so the technique does nothing.
  • Ignoring reduced-motion preferences. Looping or large animation with no prefers-reduced-motion fallback can genuinely make some users unwell.
  • Reaching for a JavaScript library too early. A spinner does not need a dependency. Add weight only when the animation is complex enough to justify it.
  • Looping attention-grabbing motion forever. A notification shake or a bouncing arrow should play once or a few times, then stop. Endless motion is fatiguing.

Frequently asked questions

CSS is the easiest and most common approach. Inline the SVG in your HTML, then use a CSS transition for hover effects or a keyframes rule for looping motion, targeting properties like transform and opacity. It uses skills you already have, works in every modern browser, and does not require any JavaScript.
Use CSS when the SVG lives in your HTML and the animation is triggered by the page, such as a hover state or a spinner. Use SMIL, the animate elements written inside the SVG itself, when you need the animation to travel with the file, for example an icon used as an img or shared across projects. CSS covers most day-to-day cases.
No. SMIL was once flagged for possible removal in Chrome, but that decision was reversed and it remains supported across current browsers. It is a reasonable choice when you specifically need self-contained animation baked into the SVG file. For animation driven by the page, CSS is usually the simpler option.
Use the line-drawing technique. Set stroke-dasharray and stroke-dashoffset on the path equal to its length, which hides the stroke, then animate stroke-dashoffset to zero so the line appears to draw. It only works on stroked paths, not solid fills, and you can read the path length in the browser with getTotalLength.
Wrap your animations in a prefers-reduced-motion media query. Browsers expose whether the user has asked for reduced motion in their system settings, and you can use that to shorten, simplify or switch off animation for those users while keeping it for everyone else. It is a small amount of CSS and an accessibility expectation.
Not with your page CSS. When an SVG is referenced through an img tag or a CSS background, your stylesheet cannot reach inside it. To animate it you either inline the SVG in the HTML so CSS can target its parts, or put SMIL animate elements inside the SVG file so the motion is self-contained.

Put it into practice: grab an animated icon from the tool, learn the ways to place an SVG, recolour it to match your brand, and make sure it stays accessible and lightweight before it ships.

Grab an animated icon to start with

The ICON OOP tool includes ready-made animated SVGs plus 23,000+ static icons and logos. Search, customise and download in seconds.

Open the ICON OOP tool