SVG to React: cleanup and JSX patterns
Published: 2026-09-05
Why design-tool SVG needs SVGO cleanup, how HTML attributes become camelCase JSX props, and when to export a React component versus optimized markup.
Exporting an icon from Figma, Illustrator, or a stock pack rarely produces React-ready markup. You get editor metadata, long decimal path data, class instead of className, and kebab-case attributes that JSX rejects. Turning that into a clean component is mostly two steps: optimize the SVG, then serialize it with React’s attribute rules.
What “cleanup” usually removes
Design tools and older exporters often leave baggage that browsers ignore but that inflates bundles and confuses diffs:
| Typical cruft | Why remove it |
|---|---|
| Editor comments / generator stamps | Noise in git; no runtime value |
Unused id, defs, or hidden layers |
Dead weight after flattening |
| Excess path precision | 1.000000 vs 1 — same shape, fewer bytes |
| Default fill/stroke when CSS will own color | Easier theming with currentColor |
SVGO (and similar optimizers) apply those transforms as a pipeline of plugins. Aggressive presets shrink more but can drop titles, descriptions, or IDs you still need for accessibility or CSS hooks—choose a milder profile when those matter. For minify-only workflows without a React wrapper, see the SVG Minifier.
HTML SVG attributes vs JSX
SVG in HTML uses kebab-case attribute names and HTML-ish spellings. React’s JSX layer expects camelCase DOM property names (same idea as converting identifiers between case styles):
| In SVG / HTML | In JSX |
|---|---|
class |
className |
stroke-width |
strokeWidth |
fill-rule |
fillRule |
clip-path |
clipPath |
xlink:href |
xlinkHref (legacy) |
xml:space |
xmlSpace |
Numeric-looking values often become strings in markup (width="24"); JSX may keep them as strings or numbers depending on your serializer. Self-closing tags (<path … />) are normal in JSX even when the original SVG used paired tags.
Pasting raw SVG into a .jsx file without renaming attributes is a common compile error. Converters that walk the DOM and rewrite names avoid that busywork.
Component patterns that stay maintainable
A practical React icon component:
- Named export (
export function IconName) so tree-shaking and imports stay clear. - Spread
…propson the root<svg>so callers can passclassName,aria-*,width/height, orstylewithout editing the file. - TypeScript: type props as
SVGProps<SVGSVGElement>so IDE autocomplete matches native SVG attributes. - Prefer
currentColor(or omit fill) so the icon inherits text color from CSS instead of hard-coded hex from the design tool.
Example shape (illustrative):
import type { SVGProps } from 'react';
export function CheckIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path d="…" stroke="currentColor" strokeWidth={2} />
</svg>
);
}
Keep one icon per file (or a small barrel) unless you deliberately ship a sprite sheet. Inline components beat data-URL blobs when you need theming and accessibility props.
JSX, TSX, or optimized SVG string?
| Output | Best when |
|---|---|
| JSX component | App uses JavaScript; no type imports needed |
| TSX component | TypeScript React app; you want SVGProps on the root |
| Optimized SVG | You still need a .svg asset, CSS mask, or a non-React consumer |
Optimization first, then format choice, keeps path data and attributes consistent across all three.
Why run this locally
SVG exports sometimes include client logos, unreleased UI, or path data that encodes product screens. Uploading those to a random “SVG → React” site is an unnecessary leak. A browser tool that runs SVGO in a Web Worker and converts with DOMParser on-device keeps the markup in your tab—see Why “local only” matters for developer tools.
SVG is XML under the hood; for general well-formedness and pretty-print of arbitrary XML (not React-specific), see Validating and formatting XML in the browser.
Try it on LocalTools
Paste or drop an .svg into SVG to JSX: SVGO cleans the markup in a worker, then you choose JSX, TSX (with SVGProps), or optimized SVG and copy the result—nothing is uploaded.
Related reading
- SVG Minifier for size-focused SVGO profiles without a React wrapper
- Text case styles: camelCase, snake_case, kebab-case
- Why “local only” matters for developer tools
- Validating and formatting XML in the browser