Converting SVG to React components involves transforming HTML attributes to JSX (class to className, stroke-width to strokeWidth), removing unnecessary metadata, and wrapping the SVG in a reusable component with typed props. Tools like SVGR automate this process. Optimize SVGs with SVGO first to reduce file size by 20-60%. For accessibility, add aria-label or aria-hidden depending on whether the icon is decorative or meaningful. Use our free SVG to JSX converter for instant, production-ready React components.
- JSX requires camelCase SVG attributes: stroke-width becomes strokeWidth, fill-rule becomes fillRule, clip-path becomes clipPath.
- SVGR is the industry-standard tool for converting SVG files into React components, used by Create React App and Next.js.
- Always run SVGO before converting to React. It removes editor metadata and can reduce SVG file size by 20-60%.
- Inline SVG components offer full control over styling, animation, and accessibility compared to img tags or CSS backgrounds.
- For icon libraries with 50+ icons, consider SVG sprites or tree-shakable icon packages to minimize bundle size.
- Decorative icons need aria-hidden="true"; meaningful standalone icons need role="img" and aria-label.
Try our free SVG to JSX/React converter →
1. Why Convert SVG to React Components?
SVG (Scalable Vector Graphics) is the dominant format for icons, logos, and illustrations in modern web applications. Unlike raster images (PNG, JPEG, WebP), SVGs are resolution-independent, style-able with CSS, and animatable. But using SVG in React is not as simple as copying the markup.
There are three ways to use SVG in React: <img src="icon.svg">, CSS background images, and inline SVG as JSX. Inline SVG is the most powerful because it gives you:
- Full CSS control: Style individual paths, groups, and elements with CSS classes or inline styles
- Dynamic props: Change colors, sizes, and stroke widths via React props at runtime
- Animation: Animate specific parts of the SVG using CSS transitions, Framer Motion, or GSAP
- Accessibility: Add ARIA attributes, titles, and descriptions directly to SVG elements
- Event handling: Attach onClick, onHover, and other event handlers to SVG elements
The trade-off is that inline SVG becomes part of your JavaScript bundle. For small icons (under 2KB each), this is negligible. For large, complex illustrations, consider using <img> tags with external SVG files that can be cached separately by the browser.
2. SVG Attribute Differences in JSX
The biggest friction when moving SVG into React is attribute name conversion. SVG uses kebab-case attributes (as defined in the SVG spec), but JSX requires camelCase because it maps to JavaScript DOM properties. Here is the complete list of attributes you need to convert:
| SVG / HTML Attribute | JSX Equivalent |
|---|---|
class | className |
stroke-width | strokeWidth |
stroke-linecap | strokeLinecap |
stroke-linejoin | strokeLinejoin |
stroke-dasharray | strokeDasharray |
stroke-dashoffset | strokeDashoffset |
fill-rule | fillRule |
fill-opacity | fillOpacity |
clip-path | clipPath |
clip-rule | clipRule |
font-size | fontSize |
text-anchor | textAnchor |
xlink:href | xlinkHref |
xmlns:xlink | xmlnsXlink |
color-interpolation | colorInterpolation |
dominant-baseline | dominantBaseline |
stop-color | stopColor |
stop-opacity | stopOpacity |
Beyond attribute names, there are a few other syntax changes required:
style="fill:red;stroke:blue"must becomestyle={{ fill: "red", stroke: "blue" }}(object syntax)- Self-closing tags like
<path>must use<path />in JSX xmlns="http://www.w3.org/2000/svg"is optional in JSX (React adds it automatically)- Comments
<!-- ... -->must use{/* ... */}syntax
Missing even one attribute conversion will trigger a React console warning like <code>Warning: Invalid DOM property</code> and may cause rendering issues.
3. Converting SVG Manually vs. Automated Tools
For a single icon, manual conversion is straightforward: rename the attributes, remove unnecessary metadata, and wrap in a component. Here is the step-by-step process:
- Step 1: Copy the SVG markup from your design tool (Figma, Sketch, Illustrator) or SVG file.
- Step 2: Remove unnecessary attributes like xmlns, xml:space, editor-specific data attributes, and comments.
- Step 3: Rename kebab-case attributes to camelCase (see the table above).
- Step 4: Convert inline styles from strings to JavaScript objects.
- Step 5: Wrap in a React component with TypeScript props for size, color, and pass-through SVG attributes.
This works fine for 1-5 icons, but becomes tedious and error-prone for larger sets. Automated tools like SVGR handle all these transformations plus optimization in a single step.
Here is what a manual conversion looks like:
// Before: Raw SVG from Figma
<svg xmlns="http://www.w3.org/2000/svg" class="icon"
width="24" height="24" viewBox="0 0 24 24"
fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<path d="M20 6L9 17l-5-5"/>
</svg>
// After: React Component
interface CheckIconProps extends React.SVGProps<SVGSVGElement> {
size?: number;
}
function CheckIcon({ size = 24, ...props }: CheckIconProps) {
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
{...props}
>
<path d="M20 6L9 17l-5-5" />
</svg>
);
}4. SVGR: The Industry-Standard SVG-to-React Transformer
SVGR is the de-facto standard tool for converting SVG files into React components. It is used internally by Create React App, Next.js, Vite (via plugins), and most major React toolchains. SVGR handles:
- Attribute conversion (kebab-case to camelCase)
- Style string to object conversion
- xmlns removal
- Title element injection for accessibility
- Ref forwarding for DOM access
- TypeScript type generation
- Integration with SVGO for optimization
You can use SVGR in multiple ways:
CLI Usage
# Install SVGR CLI
npm install -D @svgr/cli
# Convert a single SVG file
npx @svgr/cli icon.svg
# Convert all SVGs in a directory to React components
npx @svgr/cli --out-dir src/components/icons src/assets/svg/
# With TypeScript support
npx @svgr/cli --typescript --out-dir src/icons src/svg/
# With SVGO optimization
npx @svgr/cli --svgo --out-dir src/icons src/svg/Webpack / Next.js Integration
// next.config.js
module.exports = {
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
});
return config;
},
};
// Now you can import SVGs as React components:
import Logo from './logo.svg';
function Header() {
return <Logo width={120} height={40} />;
}Vite Integration
// vite.config.ts
import svgr from 'vite-plugin-svgr';
export default defineConfig({
plugins: [react(), svgr()],
});
// Import as React component
import { ReactComponent as Logo } from './logo.svg';
// Or with default export
import Logo from './logo.svg?react';SVGR also supports custom templates, allowing you to define exactly how the output component should look. This is useful for enforcing consistent patterns across large icon libraries.
5. SVG Optimization with SVGO Before Converting
SVGO (SVG Optimizer) is a Node.js tool that optimizes SVG files by removing redundant information, collapsing groups, merging paths, and cleaning up metadata. Running SVGO before converting to React components can reduce SVG size by 20-60%.
What SVGO removes and optimizes:
- Editor metadata: Inkscape, Illustrator, and Sketch data attributes, comments, and processing instructions
- Redundant attributes: Default values that browsers apply automatically (fill="black", stroke="none")
- Empty elements: Groups, defs, and containers with no visible content
- Precision: Reduces coordinate precision from 15 decimal places to 2-3 without visible quality loss
- Path optimization: Merges adjacent path segments and simplifies curves
- ID cleanup: Removes or minifies auto-generated IDs that cause conflicts when multiple SVGs are inlined
SVGO is built into SVGR, so if you use SVGR, you get optimization for free. You can also use SVGO standalone:
# Install SVGO
npm install -D svgo
# Optimize a single file
npx svgo input.svg -o output.svg
# Optimize all SVGs in a folder
npx svgo -f ./src/assets/svg/ -o ./src/assets/svg-optimized/
# With custom config (svgo.config.js)
module.exports = {
plugins: [
'preset-default',
'removeDimensions', // Remove width/height, keep viewBox
'removeXMLNS', // Remove xmlns for inline use
{ name: 'removeAttrs', // Remove specific attributes
params: { attrs: '(data-.*)' }
},
{ name: 'sortAttrs' }, // Consistent attribute order
],
};
# Before SVGO: 2,847 bytes
# After SVGO: 892 bytes (68% reduction)6. Creating Reusable SVG Icon Components with Props
A well-designed SVG icon component should be flexible enough to use anywhere in your application. The key principles are:
- Accept size prop: A single number that sets both width and height (icons are typically square)
- Accept color prop: Default to currentColor so the icon inherits the parent text color
- Spread remaining props: Use
...restto forward className, onClick, aria-label, data attributes, etc. - Use TypeScript: Extend
React.SVGProps<SVGSVGElement>for full type safety - Forward refs: Use
React.forwardRefwhen consumers need DOM access for animations or measurements
Here is a production-ready icon component pattern:
import React from 'react';
interface IconProps extends React.SVGProps<SVGSVGElement> {
size?: number;
color?: string;
title?: string;
}
const HeartIcon = React.forwardRef<SVGSVGElement, IconProps>(
({ size = 24, color = 'currentColor', title, ...rest }, ref) => (
<svg
ref={ref}
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke={color}
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
role={title ? 'img' : undefined}
aria-label={title}
aria-hidden={title ? undefined : true}
{...rest}
>
{title && <title>{title}</title>}
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
</svg>
)
);
HeartIcon.displayName = 'HeartIcon';
export default HeartIcon;Usage examples:
// Basic usage - inherits parent text color
<HeartIcon />
// Custom size and color
<HeartIcon size={32} color="#ef4444" />
// With accessibility label
<HeartIcon title="Add to favorites" />
// With className for Tailwind
<HeartIcon className="text-red-500 hover:text-red-600 transition-colors" />
// With event handler
<HeartIcon onClick={() => toggleFavorite()} style={{ cursor: 'pointer' }} />
// With ref for animations
const iconRef = useRef<SVGSVGElement>(null);
<HeartIcon ref={iconRef} />7. SVG Sprites vs. Inline SVG vs. img Tags
There are three main strategies for using SVG in React applications. Each has distinct trade-offs:
Inline SVG Components (Recommended for most apps)
Each icon is a separate React component with the SVG markup inlined. This is the approach we have been discussing throughout this guide.
Pros: Full styling control, dynamic props, animation support, tree-shakable, great DX
Cons: Adds to JS bundle size, cannot be cached separately from JS
SVG Sprite with symbol and use
All icons are defined once in a single SVG sprite file using <symbol> elements. Individual icons reference them with <use href="#icon-name">.
<!-- sprites.svg -->
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<symbol id="icon-heart" viewBox="0 0 24 24">
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0..." />
</symbol>
<symbol id="icon-star" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87..." />
</symbol>
</svg>
<!-- Usage -->
<svg width="24" height="24">
<use href="#icon-heart" />
</svg>Pros: Icons defined once in the DOM, smaller JS bundle, browser caches the sprite
Cons: Limited styling (no per-instance path colors), more complex setup, no tree-shaking
img Tag with External SVG Files
SVG files are loaded as static assets, just like PNG or JPEG images.
// External SVG via img tag
<img src="/icons/logo.svg" alt="Company logo" width={120} height={40} />
// Next.js Image component
import Image from 'next/image';
<Image src="/icons/logo.svg" alt="Logo" width={120} height={40} />Pros: Cacheable, no JS bundle impact, good for large complex SVGs
Cons: No styling control, no animation, extra HTTP requests, no dynamic colors
Rule of thumb: Use inline SVG components for interactive, styled icons (most apps). Use sprites for large icon sets (100+ icons). Use img tags for complex illustrations and decorative images.
8. Accessibility: aria-label, role="img", title Element
SVG accessibility is often overlooked but is critical for screen reader users. The correct approach depends on the icon context:
Decorative Icons (next to text labels)
When an icon appears alongside visible text (like a button with label text), the icon is decorative. Hide it from screen readers:
// Decorative icon next to text - hide from screen readers
<button>
<svg aria-hidden="true" focusable="false" width="16" height="16" viewBox="0 0 24 24">
<path d="M19 7l-7 7-7-7" />
</svg>
<span>Show more</span>
</button>Meaningful Icons (standalone, no text)
When an icon conveys meaning without accompanying text (like a standalone close button), it needs an accessible name:
// Meaningful icon - needs accessible name
<button aria-label="Close dialog">
<svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24">
<path d="M18 6L6 18M6 6l12 12" />
</svg>
</button>
// Standalone meaningful icon (no button wrapper)
<svg role="img" aria-label="Warning: action required" width="24" height="24" viewBox="0 0 24 24">
<path d="M12 2L2 22h20L12 2zm0 4l7.5 14h-15L12 6z" />
<path d="M12 10v4m0 2v2" />
</svg>Using the title Element
The SVG <title> element provides a text description that some screen readers announce. Link it with aria-labelledby for reliable support:
// Using <title> with aria-labelledby for broadest support
<svg
role="img"
aria-labelledby="icon-title-settings"
width="24" height="24"
viewBox="0 0 24 24"
>
<title id="icon-title-settings">Settings</title>
<path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z" />
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l..." />
</svg>A reusable pattern that handles all three cases:
interface AccessibleIconProps extends React.SVGProps<SVGSVGElement> {
size?: number;
color?: string;
label?: string; // If provided, icon is "meaningful"
children: React.ReactNode; // SVG paths
}
function AccessibleIcon({ size = 24, color = 'currentColor', label, children, ...rest }: AccessibleIconProps) {
const isDecorative = !label;
return (
<svg
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke={color}
strokeWidth={2}
role={isDecorative ? undefined : 'img'}
aria-label={isDecorative ? undefined : label}
aria-hidden={isDecorative ? true : undefined}
focusable={isDecorative ? false : undefined}
{...rest}
>
{children}
</svg>
);
}9. SVG Animation in React (CSS, Framer Motion, GSAP)
One of the biggest advantages of inline SVG is the ability to animate individual elements. Here are the three most common approaches:
CSS Animations
The simplest approach. Use CSS keyframes or transitions to animate SVG attributes like opacity, transform, stroke-dashoffset, and fill:
// CSS animation: spinning loader
function SpinnerIcon({ size = 24 }: { size?: number }) {
return (
<>
<style>{`
@keyframes spin { to { transform: rotate(360deg); } }
.svg-spinner { animation: spin 1s linear infinite; }
`}</style>
<svg className="svg-spinner" width={size} height={size} viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="10" stroke="#e2e8f0" strokeWidth="3" />
<path d="M12 2a10 10 0 0 1 10 10" stroke="#3b82f6" strokeWidth="3" strokeLinecap="round" />
</svg>
</>
);
}
// CSS transition: stroke animation on hover
function AnimatedCheck({ size = 48 }: { size?: number }) {
return (
<>
<style>{`
.check-path {
stroke-dasharray: 30;
stroke-dashoffset: 30;
transition: stroke-dashoffset 0.4s ease;
}
.check-icon:hover .check-path {
stroke-dashoffset: 0;
}
`}</style>
<svg className="check-icon" width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2">
<circle cx="12" cy="12" r="10" opacity="0.2" />
<path className="check-path" d="M8 12l3 3 5-5" strokeLinecap="round" />
</svg>
</>
);
}Framer Motion
Framer Motion provides the best React-specific SVG animation experience with declarative motion components. It supports path morphing, stagger effects, and spring physics:
import { motion } from 'framer-motion';
// Animated path drawing with Framer Motion
function AnimatedHeart() {
return (
<svg width="48" height="48" viewBox="0 0 24 24" fill="none">
<motion.path
d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"
stroke="#ef4444"
strokeWidth={2}
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{ duration: 2, ease: 'easeInOut' }}
/>
</svg>
);
}
// SVG with hover scale and color change
function InteractiveIcon() {
return (
<motion.svg
width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor"
whileHover={{ scale: 1.2, stroke: '#3b82f6' }}
whileTap={{ scale: 0.9 }}
transition={{ type: 'spring', stiffness: 300 }}
>
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
</motion.svg>
);
}GSAP (GreenSock)
GSAP is the most powerful option for complex, timeline-based SVG animations. It handles SVG transforms consistently across browsers and provides fine-grained control:
import { useRef, useEffect } from 'react';
import { gsap } from 'gsap';
function GsapSvgAnimation() {
const pathRef = useRef<SVGPathElement>(null);
const circleRef = useRef<SVGCircleElement>(null);
useEffect(() => {
const tl = gsap.timeline({ repeat: -1, yoyo: true });
tl.from(pathRef.current, {
strokeDashoffset: 100,
strokeDasharray: 100,
duration: 1.5,
ease: 'power2.inOut',
});
tl.to(circleRef.current, {
scale: 1.2,
transformOrigin: 'center',
duration: 0.5,
ease: 'elastic.out(1, 0.5)',
}, '-=0.5');
}, []);
return (
<svg width="64" height="64" viewBox="0 0 24 24" fill="none">
<circle ref={circleRef} cx="12" cy="12" r="10" stroke="#3b82f6" strokeWidth="1.5" />
<path ref={pathRef} d="M8 12l3 3 5-5" stroke="#22c55e" strokeWidth="2" strokeLinecap="round" />
</svg>
);
}Choose CSS for simple hover effects and loading spinners. Choose Framer Motion for React-idiomatic animations with gestures. Choose GSAP for complex, sequenced animations and SVG morphing.
10. Performance: SVG vs. PNG vs. WebP for Icons
Choosing the right format for icons impacts both performance and visual quality:
| Format | Typical Icon Size | Scalable | CSS Stylable | Animatable | Best For |
|---|---|---|---|---|---|
| SVG | 200-800 B | Yes | Yes | Yes | Icons, logos, UI graphics |
| PNG | 1-5 KB (@2x) | No | No | No | Photos, complex images |
| WebP | 0.5-3 KB | No | No | Limited | Photos (smaller than PNG) |
| Icon Font | 50-200 KB (full set) | Yes | Color only | No | Legacy projects |
For icons and simple graphics, SVG is almost always the best choice. A typical icon is 200-800 bytes as optimized SVG, compared to 1-5KB as PNG at multiple resolutions. SVGs also look crisp on any screen density without serving multiple sizes.
Performance tips for SVG in React:
- Optimize with SVGO: Remove unnecessary metadata before converting to components
- Lazy load complex SVGs: Use React.lazy() or dynamic imports for illustrations over 5KB
- Avoid inline SVG for large graphics: Maps, detailed illustrations, and complex charts should use img tags
- Use currentColor: Avoids duplicate SVGs with different color variants
- Minimize path complexity: Simpler paths render faster and produce smaller files
11. Tree-Shaking SVG Icon Libraries
When using icon libraries in React (Lucide, Heroicons, Phosphor, React Icons), tree-shaking ensures only the icons you actually import are included in your production bundle. This is critical because icon libraries can contain thousands of icons totaling several megabytes.
To enable effective tree-shaking, use named imports from specific module paths:
// GOOD: Tree-shakable named imports
import { Heart, Star, Search } from 'lucide-react';
import { ArrowRightIcon, CheckIcon } from '@heroicons/react/24/outline';
// BAD: Barrel import that may include entire library
import * as Icons from 'lucide-react'; // All 1000+ icons bundled
// GOOD: Import from specific sub-package (react-icons)
import { FaHeart } from 'react-icons/fa';
import { HiSearch } from 'react-icons/hi';
// BAD: Import from root (no tree-shaking)
import { FaHeart } from 'react-icons'; // May bundle all icon setsHere is how popular React icon libraries compare for tree-shaking:
| Library | Total Icons | Tree-Shakable | Per-Icon Size |
|---|---|---|---|
| Lucide React | 1,400+ | Yes | ~500 B |
| Heroicons | 300+ | Yes | ~400 B |
| Phosphor Icons | 7,000+ | Yes | ~600 B |
| React Icons | 40,000+ | Per sub-package | ~300-800 B |
If your app uses only 10-20 icons from a large library, the difference between tree-shaking and importing the entire library can be 50KB vs. 2MB+ in your production bundle.
Tip: Some bundlers (especially older webpack configs) do not tree-shake properly when you use barrel imports like import { Heart } from "react-icons". Always import from the specific sub-package: import { FaHeart } from "react-icons/fa".
Try our free SVG to JSX/React converter
/ja/tools/svg-to-jsx →Frequently Asked Questions
What is the easiest way to convert SVG to a React component?
The easiest way is to use an online SVG to JSX converter tool. Paste your SVG code and get a clean React component instantly. For batch conversion of multiple SVG files, use the SVGR CLI: npx @svgr/cli --out-dir src/icons src/svg/
Should I use inline SVG or an SVG sprite system in React?
For most React applications with fewer than 50 icons, inline SVG components are simpler and more flexible. Each icon is a tree-shakable component with full styling and animation control. For large icon sets (100+ icons), consider an SVG sprite to reduce JavaScript bundle size. Both approaches work well with React.
Does inline SVG increase my JavaScript bundle size significantly?
For typical icons (200-800 bytes each), the impact is minimal. 20 optimized inline SVG icons add roughly 8-15KB to your bundle before gzip compression. For large complex SVGs (maps, illustrations), use img tags or dynamic imports with React.lazy() to avoid bloating the initial bundle.
What is the difference between SVGR and SVGO?
SVGO (SVG Optimizer) is a standalone tool that optimizes SVG files by removing metadata, simplifying paths, and reducing file size. SVGR (SVG to React) is a tool that converts SVG files into React components, handling attribute conversion, TypeScript types, and component wrapping. SVGR uses SVGO internally for optimization. You can use SVGO alone for optimization, but SVGR gives you both optimization and React component generation.
How do I make SVG icons accessible in React?
For decorative icons (next to text labels), add aria-hidden="true" and focusable="false" to hide them from screen readers. For meaningful icons (standalone, conveying information), add role="img" and aria-label="description". For icons inside buttons, the button should have the aria-label while the SVG gets aria-hidden="true".
Can I animate SVG in React? What library should I use?
Yes, inline SVG in React gives you full access to animate individual paths and elements. Use CSS animations for simple effects (hover, loading spinners). Use Framer Motion for React-idiomatic animations with gesture support. Use GSAP for complex timeline-based animations and SVG morphing. All three approaches work with inline SVG components.
Why does React show warnings when I paste SVG code?
React shows "Invalid DOM property" warnings because SVG attributes use kebab-case (stroke-width, fill-rule, clip-path) while JSX requires camelCase (strokeWidth, fillRule, clipPath). You also need to change class to className and for to htmlFor. Use an SVG to JSX converter tool to handle all these transformations automatically.
How do I use SVGR with Next.js or Vite?
For Next.js, install @svgr/webpack and add a webpack rule in next.config.js to handle .svg file imports as React components. For Vite, install vite-plugin-svgr and add it to your vite.config.ts plugins array. Both allow you to import SVG files directly as React components: import Logo from "./logo.svg".