GuideMay 2026·10 min read

SVG Animation in React — CSS, SMIL, and Framer Motion Compared

React gives you three distinct paths for animated SVG: CSS keyframes on inline SVG, SMIL baked into the file, and a motion library like Framer Motion. Each trades off bundle size, capability, and complexity differently. Here is when to reach for each.

Quick Comparison

ApproachRuntimeBundleBest for
CSS @keyframes (inline SVG)None0 KBUI icons, loaders, decorative motion
SMIL (baked into SVG file)None0 KBStandalone SVGs, emails, img tags
Framer MotionYes~45 KB gzippedComplex sequences, gestures, layout animation

Approach 1 — CSS @keyframes on Inline SVG

The simplest approach. Render the SVG inline in JSX, add a className to any element inside it, and write standard CSS. No extra package. No runtime cost. Works with CSS Modules, Tailwind, or plain stylesheets.

// SpinnerIcon.tsx
export function SpinnerIcon() {
  return (
    <svg viewBox="0 0 24 24" width="24" height="24" className="animate-spin-svg">
      <circle
        cx="12" cy="12" r="10"
        fill="none"
        stroke="currentColor"
        strokeWidth="2"
        strokeDasharray="16 47"
        strokeLinecap="round"
      />
    </svg>
  );
}
/* globals.css */
.animate-spin-svg {
  transform-origin: 50% 50%;
  animation: spin 800ms linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

When to use this

Always, for looping or simple keyframe animations on icons and loaders. Zero cost, works with SSR, no hydration issues.

Approach 2 — SMIL Exported as a React Component

SMIL animations are declared inside the SVG with <animate> and <animateTransform> elements. When you export from CSSVG as React JSX, you get a component where the animation is entirely self-contained — no CSS file needed.

// CSSVG React JSX export — animation lives inside the SVG
export function PulseIcon() {
  return (
    <svg viewBox="0 0 100 100" width="60" height="60">
      <circle cx="50" cy="50" r="20" fill="#6366f1">
        <animate
          attributeName="r"
          values="20;30;20"
          dur="1200ms"
          repeatCount="indefinite"
          calcMode="spline"
          keySplines="0.4 0 0.6 1; 0.4 0 0.6 1"
        />
        <animate
          attributeName="opacity"
          values="1;0.4;1"
          dur="1200ms"
          repeatCount="indefinite"
          calcMode="spline"
          keySplines="0.4 0 0.6 1; 0.4 0 0.6 1"
        />
      </circle>
    </svg>
  );
}

When to use this

When you want a self-contained component with no external CSS dependency — ideal for component libraries and design systems where encapsulation matters.

Approach 3 — Framer Motion

Framer Motion gives you orchestration, gesture-driven animation, exit animations, and layout transitions that CSS keyframes cannot do. But it adds ~45 KB gzipped to your bundle and requires a client component in Next.js App Router.

"use client";
import { motion } from "framer-motion";

export function CheckIcon({ checked }: { checked: boolean }) {
  return (
    <svg viewBox="0 0 24 24" width="24" height="24">
      <motion.path
        d="M4 13 L9 18 L20 7"
        fill="none"
        stroke="#6366f1"
        strokeWidth="2"
        strokeLinecap="round"
        initial={{ pathLength: 0, opacity: 0 }}
        animate={checked
          ? { pathLength: 1, opacity: 1 }
          : { pathLength: 0, opacity: 0 }}
        transition={{ duration: 0.4, ease: "easeOut" }}
      />
    </svg>
  );
}

When to use this

State-driven transitions (checked/unchecked, open/close), gesture responses (drag, hover with spring physics), or exit animations. Not worth the bundle cost for looping icons.

Generating the SVG Code Without Hand-Coding

For CSS and SMIL approaches, CSSVG lets you design the animation visually and then export directly as a React component — no manual keyframe math required.

  • Import your SVG icon — each shape becomes its own layer in the timeline
  • Set keyframes by scrubbing the timeline and adjusting properties in the Inspector
  • Export → React JSX gets you a self-contained component with SMIL animations
  • Export → CSS gets you the SVG plus a CSS file you can import into your component

Decision Guide

Looping icon or loader with no JS interaction?

CSS @keyframes — zero cost, works everywhere.

Self-contained component for a design system?

SMIL via CSSVG React export — no CSS dependency.

Animation driven by React state or user gesture?

Framer Motion — accept the bundle size tradeoff.

SVG needs to animate in an email or img tag?

SMIL only — CSS is sandboxed in those contexts.

Export Animated SVG for React — Free

Design your animation visually and export a ready-to-paste React component. No hand-coding required.

Open the Editor — it's free