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.

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.

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