Logo

SVG to TSX

Convert SVG into a typed React component with an exported props interface

Paste your SVG

Options

TSX output

Paste an SVG on the left to see the converted component…

Typed Props, Not Just a Renamed File

You rename your icon to .tsx and TypeScript starts shouting about implicit any. Adding types by hand for every icon gets old by the third one. Our SVG to TSX converter writes the interface for you.

You get an exported props type extending every SVG attribute, a type-only import, and full autocomplete at the call site.

How to Convert SVG to a TypeScript React Component

  1. Paste the SVG. The preview confirms it parsed.
  2. Use a PascalCase name. It names both the function and the interface.
  3. Leave prop forwarding on. Without it the typed interface has nowhere to go.
  4. Remove the fixed size if CSS should control it.
  5. Copy into a .tsx file. Nothing else to add.

That third step matters more than it looks. An interface promising every SVG attribute is a lie unless those props actually reach the element.

What React SVGProps Actually Gives You

The generated interface extends the full SVG attribute set:

import type { SVGProps } from 'react';

export interface IconProps extends SVGProps<SVGSVGElement> {
  title?: string;
}

That one line buys a lot. Callers get autocomplete on className, onClick, fill, strokeWidth, aria-label and everything else, all correctly typed.

The SVGSVGElement part is the piece people get wrong. It is not HTMLElement — an SVG root has its own DOM interface, and using the wrong one breaks ref typing later.

We add an optional title prop for accessibility, which we come back to below.

Why the Import Is Type-Only

Notice import type rather than a plain import. That is deliberate and it earns its place.

A type-only import is erased at compile time, so nothing about React enters your runtime bundle from this file. It also signals to bundlers that the module has no side effects.

That second point matters for icon sets. Import a hundred icons through a barrel file and a bundler that cannot prove they are side-effect free will ship all of them.

In our experience that is the single most expensive mistake in a component library — hundreds of kilobytes of icons nobody rendered.

Making a Typed SVG Icon Accessible

A decorative icon should be hidden from screen readers. One that carries meaning needs a label.

// Decorative — hide it
<SearchIcon aria-hidden="true" />

// Meaningful — label it
<SearchIcon role="img" aria-label="Search" />

Both work because the props interface already includes ARIA attributes. Nothing extra to type.

The title prop is there for the third pattern: rendering a <title> element inside the SVG and pointing aria-labelledby at it. Add role="img" as well, or some readers skip the title entirely.

Most icon libraries ship neither. It is a small edit that makes a real difference to anyone using a screen reader.

Same Attribute Rules as Plain JSX

TypeScript changes the wrapper, not the markup. Every kebab-case attribute still has to become camelCase, because JSX compiles to JavaScript either way.

So stroke-width becomes strokeWidth, clip-path becomes clipPath, and xlink:href becomes xlinkHref. The full table lives on the SVG to JSX converter.

TypeScript does make one of those errors louder. An unrecognised attribute becomes a type error rather than a silent React warning you scroll past.

Want the reasoning behind the renaming? Our post on SVG attributes and camelCase in React covers it.

Other Targets for the Same Icon

Shipping to mobile too? The SVG to React Native converter swaps every element for its react-native-svg component, which is a far larger change than adding types.

On a Vue codebase, the SVG to Vue component converter skips the attribute rewriting entirely, because Vue templates never needed it.

Frequently Asked Questions

Why SVGProps rather than ComponentProps of svg?

+
They resolve to almost the same thing, and SVGProps says what you mean more directly. ComponentProps is the better choice when you are wrapping another component and want its props to follow along automatically.

Why does the output use import type instead of a normal import?

+
Because it is erased at compile time, so nothing about React is pulled into the runtime bundle. It also stops a bundler treating the file as having side effects, which matters for tree-shaking an icon barrel.

What is the title prop actually for?

+
Accessibility. Adding a title element inside the SVG and pointing aria-labelledby at it gives screen readers something to announce. Set role to img as well, or the title may be skipped entirely.

How do I type a forwarded ref?

+
The element type is SVGSVGElement, not HTMLElement, and that catches people. Wrap with forwardRef and pass SVGSVGElement as the first type argument, your props interface as the second.

Should I type the component as React.FC?

+
We would not any more. It adds an implicit children prop that an icon does not want, and it makes generic components awkward. A plain function with a typed parameter is clearer and behaves better.

TypeScript complains about xmlns:xlink. What now?

+
The JSX namespace has no such attribute, since the colon form is not valid there. The converter rewrites it to xlinkHref, which is typed. If your file still has the namespace declaration, delete it — a JSX component never needs one.

Do I need an svg.d.ts declaration file?

+
Only if you import .svg files directly, which is the SVGR approach. Pasting a component in as .tsx needs no declaration at all, because it is ordinary TypeScript. That is one reason hand conversion is simpler for a handful of icons.

Should props be an interface or a type alias?

+
Use an interface when you extend something, as here, because declaration merging lets consumers add to it. A type alias is fine for unions and mapped types. For icon props the interface reads better.

Why does exactOptionalPropertyTypes break my icon props?

+
That flag stops you passing undefined explicitly to an optional prop. Spreading an object where title happens to be undefined then fails type checking. Omit the key rather than setting it to undefined.

Does a barrel file of icons hurt bundle size?

+
It can, badly, if your bundler cannot prove the unused exports are side-effect free. Set sideEffects to false in package.json, or import each icon by its own path. We have seen a barrel add hundreds of kilobytes nobody used.

The Short Version

Paste, name, copy. Converting SVG to TSX gives you a real props interface rather than a renamed file with implicit any everywhere.

Keep prop forwarding on, and check your barrel file is marked side-effect free before shipping a hundred icons.

Related Tools You Might Like

Advertisement