DEV Community

albert nahas
albert nahas

Posted on • Originally published at leandine.hashnode.dev

Converting SVGs to React Components: The Right Way

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:

  1. Manual conversion (copying SVG code into JSX)
  2. Automated conversion (using tools like SVGR)
  3. 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>
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

Key tips:

  • Convert SVG attribute names to JSX style: stroke-widthstrokeWidth, classclassName, etc.
  • Make the component reusable by accepting props and spreading them onto <svg>.
  • Use currentColor for stroke and fill for 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
Enter fullscreen mode Exit fullscreen mode

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'],
      },
    ],
  },
};
Enter fullscreen mode Exit fullscreen mode

Vite (with vite-plugin-svgr):

// vite.config.ts
import svgr from 'vite-plugin-svgr';

export default {
  plugins: [svgr()],
};
Enter fullscreen mode Exit fullscreen mode

Usage in code:

import { ReactComponent as CheckmarkIcon } from './checkmark.svg';

<CheckmarkIcon width={32} height={32} color="green" />
Enter fullscreen mode Exit fullscreen mode

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 />
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode
// Only this icon is bundled
import { CheckmarkIcon } from './icons';
Enter fullscreen mode Exit fullscreen mode

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';
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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)