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
| Approach | Runtime | Bundle | Best for |
|---|---|---|---|
| CSS @keyframes (inline SVG) | None | 0 KB | UI icons, loaders, decorative motion |
| SMIL (baked into SVG file) | None | 0 KB | Standalone SVGs, emails, img tags |
| Framer Motion | Yes | ~45 KB gzipped | Complex 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