SVG icons and illustrations are a staple in modern web development, prized for their scalability, clarity, and tiny file sizes. But incorporating SVGs into React projects brings up a familiar question: what’s the best way to convert SVGs to React components? Should you use a tool like SVGR, import SVGs directly, or handcraft them as JSX? And how do these approaches affect your bundle size and tree-shaking? Let’s break down the best practices for working with SVGs in React, with code examples and the nuances you need to know.
Why Use SVGs as React Components?
SVGs offer several advantages over raster images (like PNG or JPEG), especially in React projects:
- Scalability: SVGs remain crisp at any size.
- Customizability: You can easily change colors, stroke, or other attributes with props or CSS.
- Performance: Inline SVGs reduce HTTP requests and can be optimized for tree-shaking.
But to unlock these benefits, you need an efficient workflow to convert your SVG assets into reusable React components.
Common Approaches for Using SVGs in React
There are three primary ways developers turn SVGs into React components:
- Manual conversion (copying SVG code into JSX)
- Automated conversion (using tools like SVGR)
- Importing SVGs as React components (with build-tool support)
Let’s dive into each method, including their pros, cons, and impact on tree-shaking and bundle size.
1. Manual Conversion: Handcrafting SVG Components
The simplest method is to copy the SVG markup directly into a React component, then tweak it for JSX compatibility. Here’s how you might do it:
Step-by-step Example
Suppose you have an SVG file like this:
<!-- checkmark.svg -->
<svg width="24" height="24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M5 13l4 4L19 7"/>
</svg>
Convert it to a React component:
// CheckmarkIcon.tsx
import React from "react";
const CheckmarkIcon: React.FC<React.SVGProps<SVGSVGElement>> = (props) => (
<svg
width={24}
height={24}
fill="none"
stroke="currentColor"
strokeWidth={2}
{...props}
>
<path d="M5 13l4 4L19 7" />
</svg>
);
export default CheckmarkIcon;
Key tips:
- Convert SVG attribute names to JSX style:
stroke-width→strokeWidth,class→className, etc. - Make the component reusable by accepting
propsand spreading them onto<svg>. - Use
currentColorforstrokeandfillfor easy theming.
Pros:
- Full control over the SVG code.
- No build tooling required.
Cons:
- Tedious for large icon sets.
- Prone to typos and manual errors.
- Misses out on optimization or automation benefits.
2. Automated Conversion: Using SVGR
For anything beyond a handful of icons, manual conversion quickly becomes impractical. That’s where SVGR shines. SVGR is a CLI and library that transforms raw SVG files into optimized React components.
How SVGR Works
- Converts SVG attributes to JSX automatically.
- Optimizes SVG markup (removes unnecessary code).
- Allows customization via plugins and config.
- Can be used as a CLI, Node.js library, webpack loader, or Vite/Rollup plugin.
CLI Usage Example
npx @svgr/cli checkmark.svg --out-dir src/icons
This creates a CheckmarkIcon.js (or .tsx) file, similar to the manual example above, but optimized and ready to use.
SVGR with Build Tools
SVGR can be integrated with your bundler so you can import SVGs as React components directly:
webpack (with @svgr/webpack loader):
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.svg$/,
use: ['@svgr/webpack'],
},
],
},
};
Vite (with vite-plugin-svgr):
// vite.config.ts
import svgr from 'vite-plugin-svgr';
export default {
plugins: [svgr()],
};
Usage in code:
import { ReactComponent as CheckmarkIcon } from './checkmark.svg';
<CheckmarkIcon width={32} height={32} color="green" />
Pros & Cons
Pros:
- Fast and automated for large icon sets.
- Consistent JSX conversion.
- Supports advanced features (e.g., custom templates, TypeScript, props).
- Integrates with modern build tools.
Cons:
- Adds a build step.
- Needs configuration for some setups (e.g., SSR, custom file naming).
3. Importing SVGs in React Without SVGR
Some projects, especially those using Create React App, support importing SVGs as React components out of the box. This is often powered by SVGR under the hood, but you may not realize it.
Example:
import { ReactComponent as Logo } from './logo.svg';
<Logo />
Caveats:
- Only works if your build setup supports it.
- For Next.js, you’ll need a plugin (@svgr/webpack).
- If you import SVG as a regular image (
import logo from './logo.svg'), you get a URL, not a component.
Tree-Shaking and Bundle Size: What You Need to Know
Tree-shaking is the process of removing unused code from your final bundle. When working with SVGs as React components, your approach can significantly impact bundle size and tree-shaking effectiveness.
Inline React SVG Components
When SVGs are converted to JSX and exported as components (manually or with SVGR), only the components you import are included in your final bundle. This is tree-shakable and efficient:
// src/icons/index.ts
export { default as CheckmarkIcon } from './CheckmarkIcon';
export { default as CloseIcon } from './CloseIcon';
// Only this icon is bundled
import { CheckmarkIcon } from './icons';
Bundling Large Icon Sets
If you import an entire icon pack (e.g., Material UI Icons or Heroicons), your bundle may balloon unless you import only what you use:
// Good: Only CheckIcon is bundled
import CheckIcon from '@heroicons/react/24/outline/CheckIcon';
// Bad: Everything is bundled
import * as Icons from '@heroicons/react/24/outline';
SVG as Data URLs
If you import SVGs as image URLs (import logo from './logo.svg'), tree-shaking is no longer relevant—the entire SVG file is emitted as an asset, whether you use it or not.
Optimizing SVG Assets
Regardless of your approach, always optimize your SVGs before converting them. Tools like SVGO, SVGOMG, and AI-powered generators like IcoGenie, as well as Figma’s export, can help minimize file size and remove unnecessary metadata.
Advanced: Dynamic Imports and Code Splitting
For huge icon libraries or rarely used assets, consider dynamic imports to code-split SVG components:
import React, { Suspense } from 'react';
const CheckmarkIcon = React.lazy(() => import('./icons/CheckmarkIcon'));
function MyButton() {
return (
<Suspense fallback={<span>Loading icon…</span>}>
<CheckmarkIcon />
</Suspense>
);
}
This way, icons are only loaded when needed.
Key Takeaways
- SVGs as React components are the most flexible and scalable way to use icons and illustrations in React projects.
- Manual conversion is fine for one-offs but quickly becomes unmanageable.
- SVGR and similar tools automate conversion, optimize markup, and support advanced usage.
- Build tool integration (webpack, Vite, etc.) lets you import SVGs as React components with minimal fuss.
- Tree-shaking works best when you import only the SVG components you use—avoid importing entire icon packs.
- Optimize SVGs before importing to keep your bundles lean.
- For large or infrequently used icons, dynamic import/code splitting can further reduce initial bundle size.
Choose the workflow that fits your project scale and build environment. With the right approach, your React apps can enjoy fast, maintainable, and beautiful SVG icons—without bloat.
Top comments (0)