DEV Community

arenasbob2024-cell
arenasbob2024-cell

Posted on • Originally published at viadreams.cc

SVG to JSX: The Complete Guide to Using SVGs in React

If you have ever tried to paste raw SVG markup into a React component and watched your terminal explode with errors, you are not alone. SVG and JSX look similar on the surface, but the differences are just enough to cause frustration. This guide walks through everything you need to know about using SVGs in React in 2026, from manual conversion to fully automated toolchains.

Why SVGs Belong in Your React Projects

Raster formats like PNG and JPEG have their place, but SVGs offer a set of advantages that make them the default choice for icons, logos, and illustrations in modern web apps:

  • Resolution independence. SVGs scale to any size without quality loss. A single SVG icon looks crisp on a phone screen and a 5K monitor alike.
  • Tiny file sizes. A typical icon SVG weighs 500 bytes to 2 KB, far smaller than an equivalent PNG at multiple resolutions.
  • CSS and JS control. Because SVGs are XML-based, you can style individual paths with CSS, animate them with JavaScript, and change colors dynamically through props.
  • Accessibility. SVGs support <title> and <desc> elements, letting you provide semantic meaning for screen readers.
  • Tree-shakeable when componentized. When each SVG is a React component, unused icons get stripped from your production bundle automatically.

In a React project, SVGs become first-class citizens. You can pass props to control size and color, compose them inside other components, and lazy-load them just like any other module.

The SVG-to-JSX Gap: What Actually Breaks

HTML and JSX are close relatives, but JSX follows JavaScript naming conventions. When you copy SVG code from a design tool or an icon library and drop it into a .jsx file, several things go wrong:

Attribute Name Differences

SVG uses hyphenated attribute names. JSX requires camelCase.

SVG Attribute JSX Equivalent
stroke-width strokeWidth
fill-rule fillRule
clip-path clipPath
font-size fontSize
xmlns:xlink (remove entirely)

The class vs className Problem

SVG elements exported from Figma or Illustrator often carry class attributes. In JSX, class is a reserved word. Every instance must be renamed to className.

Inline Styles Must Be Objects

SVG sometimes includes style="fill:#333;stroke:#000". In JSX, the style attribute expects a JavaScript object:

// Wrong
<circle style="fill:#333;stroke:#000" />

// Correct
<circle style={{ fill: '#333', stroke: '#000' }} />
Enter fullscreen mode Exit fullscreen mode

Self-Closing Tags

Some SVG elements like <path> and <circle> may appear without self-closing syntax in raw SVG. JSX requires every element to be explicitly closed.

Namespace Attributes

Attributes like xmlns:xlink and xml:space are invalid in JSX. They need to be removed or converted to their JSX-safe equivalents.

When you are dealing with a single simple icon, fixing these by hand takes a minute. When you have 80 icons exported from your design system, manual conversion becomes a real bottleneck.

Manual Conversion: Step by Step

For those times when you need to convert one or two SVGs quickly, here is the process:

// Original SVG from a design tool
/*
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
     class="icon" fill="none" stroke-width="2"
     stroke-linecap="round" stroke-linejoin="round">
  <path d="M12 2L2 7l10 5 10-5-10-5z"/>
  <path d="M2 17l10 5 10-5"/>
  <path d="M2 12l10 5 10-5"/>
</svg>
*/

// Converted JSX Component
const LayersIcon = ({ size = 24, color = "currentColor", ...props }) => (
  <svg
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
    width={size}
    height={size}
    className="icon"
    fill="none"
    stroke={color}
    strokeWidth={2}
    strokeLinecap="round"
    strokeLinejoin="round"
    {...props}
  >
    <path d="M12 2L2 7l10 5 10-5-10-5z" />
    <path d="M2 17l10 5 10-5" />
    <path d="M2 12l10 5 10-5" />
  </svg>
);

export default LayersIcon;
Enter fullscreen mode Exit fullscreen mode

The changes: class became className, hyphenated attributes became camelCase, stroke and size became dynamic props, and all paths are self-closed. This approach works, but it does not scale.

SVGR: The Industry Standard for Automated Conversion

SVGR is the tool the React ecosystem has settled on for converting SVG files into React components at build time. It powers the SVG handling in Create React App, Next.js (via custom config), and Vite.

Installation

npm install --save-dev @svgr/cli
# or for webpack integration
npm install --save-dev @svgr/webpack
Enter fullscreen mode Exit fullscreen mode

CLI Usage

Convert a single file:

npx @svgr/cli --icon --typescript icon.svg
Enter fullscreen mode Exit fullscreen mode

Convert an entire directory into a component library:

npx @svgr/cli --icon --typescript --out-dir src/icons -- assets/svg/
Enter fullscreen mode Exit fullscreen mode

This generates one .tsx file per SVG, each exporting a React component.

Webpack / Vite Integration

For Vite projects, use the vite-plugin-svgr plugin:

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

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

Then import SVGs directly as components:

import { ReactComponent as Logo } from './logo.svg';
// or with the newer default export approach
import Logo from './logo.svg?react';

function Header() {
  return <Logo className="header-logo" aria-label="Company logo" />;
}
Enter fullscreen mode Exit fullscreen mode

Custom SVGR Configuration

Create an .svgrrc.js file at your project root for fine-grained control:

// .svgrrc.js
module.exports = {
  icon: true,
  typescript: true,
  replaceAttrValues: {
    '#000': 'currentColor',
    '#000000': 'currentColor',
  },
  svgProps: {
    role: 'img',
  },
  plugins: ['@svgr/plugin-svgo', '@svgr/plugin-jsx', '@svgr/plugin-prettier'],
  svgoConfig: {
    plugins: [
      {
        name: 'preset-default',
        params: {
          overrides: {
            removeViewBox: false,
          },
        },
      },
      'removeDimensions',
    ],
  },
};
Enter fullscreen mode Exit fullscreen mode

This configuration does several things at once:

  • Replaces hardcoded black fills with currentColor so icons inherit text color.
  • Adds role="img" for accessibility.
  • Runs SVGO optimization to strip unnecessary metadata.
  • Preserves viewBox (critical for proper scaling) while removing fixed width/height.

Inline SVG vs. Component vs. Image Tag: Choosing the Right Approach

There are three common ways to use SVGs in React. Each has trade-offs.

1. Inline SVG (JSX directly in components)

function Alert() {
  return (
    <svg viewBox="0 0 24 24" width={20} height={20} fill="currentColor">
      <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 ..." />
    </svg>
  );
}
Enter fullscreen mode Exit fullscreen mode

Pros: Full styling control, no extra HTTP request, tree-shakeable.
Cons: Bloats component files, harder to maintain at scale.

2. SVG as a React Component (via SVGR)

import AlertIcon from '@/icons/AlertIcon';

function Alert() {
  return <AlertIcon size={20} color="red" aria-label="Warning" />;
}
Enter fullscreen mode Exit fullscreen mode

Pros: Clean imports, reusable, prop-driven, tree-shakeable.
Cons: Adds to JavaScript bundle size (each icon is a module).

3. SVG as an Image Source

function Logo() {
  return <img src="/images/logo.svg" alt="Company Logo" width={120} />;
}
Enter fullscreen mode Exit fullscreen mode

Pros: Cached by the browser, does not increase JS bundle.
Cons: No dynamic styling, no access to internal paths, extra HTTP request.

The recommendation for 2026: Use SVGR components for icons and interactive SVGs. Use <img> tags for large, static illustrations that do not need dynamic colors or animations. This gives you the best balance of performance and developer experience.

Optimization Tips for Production

Run SVGO Before Conversion

SVGs exported from design tools carry a lot of baggage: editor metadata, empty groups, redundant transforms, and unnecessary precision in path data. SVGO strips all of that.

npx svgo --multipass -f ./raw-svgs/ -o ./optimized-svgs/
Enter fullscreen mode Exit fullscreen mode

The --multipass flag runs multiple optimization passes, often shrinking files by 30-60%.

Use currentColor for Themeable Icons

Replace hardcoded color values with currentColor. This lets your icons inherit the CSS color property of their parent element, making dark mode support trivial:

// The icon color follows the parent's text color
<button className="text-red-500">
  <TrashIcon /> Delete
</button>
Enter fullscreen mode Exit fullscreen mode

Lazy-Load Heavy SVG Illustrations

For complex SVGs with hundreds of paths (maps, detailed illustrations), use React.lazy:

const WorldMap = React.lazy(() => import('./WorldMap'));

function Dashboard() {
  return (
    <Suspense fallback={<div className="map-skeleton" />}>
      <WorldMap />
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

Add Proper Accessibility Attributes

Always include either aria-label or a combination of <title> and aria-labelledby:

const CheckIcon = ({ title = "Success", ...props }) => (
  <svg role="img" aria-labelledby="check-title" {...props}>
    <title id="check-title">{title}</title>
    <path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" />
  </svg>
);
Enter fullscreen mode Exit fullscreen mode

For decorative icons that convey no meaning, use aria-hidden="true" instead.

Avoid Inlining Massive SVGs

If an SVG is over 10 KB of markup, consider keeping it as a static file served via <img> or an <object> tag. Inlining large SVGs increases your JavaScript parse time and defeats code-splitting.

Online Tools for Quick Conversions

When you need a fast, one-off conversion without setting up a build pipeline, online converters are the way to go. I keep a few bookmarked:

  • SVG to JSX Converter on VIADreams handles attribute conversion, self-closing tags, and optional TypeScript output right in the browser. Paste your SVG, get a ready-to-use React component. It is especially useful during prototyping when you are pulling icons from various sources and need instant results.

  • The SVG to JSX React Guide on VIADreams is a solid companion reference if you want to dive deeper into edge cases like gradient handling and animation preservation.

  • The SVGR Playground at react-svgr.com/playground lets you test different SVGR configurations interactively.

For production workflows, always pair these tools with a proper SVGR build integration so your entire team works from the same conversion pipeline.

Putting It All Together: A Recommended Setup

Here is a practical setup that works well for teams of any size:

  1. Design exports go into a raw-icons/ directory as plain .svg files.
  2. SVGO runs as a pre-build step to optimize them.
  3. SVGR converts optimized SVGs into TypeScript React components under src/icons/.
  4. A barrel file (src/icons/index.ts) re-exports everything for clean imports.
  5. Tree shaking ensures only the icons you actually use end up in the bundle.
// package.json scripts
{
  "scripts": {
    "icons:optimize": "svgo --multipass -f raw-icons -o optimized-icons",
    "icons:generate": "svgr --icon --typescript --out-dir src/icons -- optimized-icons",
    "icons": "npm run icons:optimize && npm run icons:generate"
  }
}
Enter fullscreen mode Exit fullscreen mode

Run npm run icons after updating any SVG assets, and your component library stays in sync with your design system automatically.

Wrapping Up

SVGs in React are straightforward once you understand the JSX conversion rules. For small projects, a quick online converter or manual fix is fine. For anything with more than a handful of icons, SVGR with SVGO gives you a reliable, automated pipeline that keeps your components clean and your bundles small.

The key takeaways:

  • Always preserve viewBox and remove fixed dimensions for flexible scaling.
  • Use currentColor to make icons theme-aware.
  • Automate conversion with SVGR in your build tool of choice.
  • Optimize with SVGO before conversion for the smallest possible output.
  • Add accessibility attributes to every icon component.

Start with the approach that fits your project size, and scale up as your icon library grows.

Top comments (0)