An SVG file is easy to drop into a browser, but a React project benefits when that same artwork ships as a component. JSX components give you typed props, tree-shaking, and a consistent API for size and color. Here is the workflow we use for the 124k doodle icons in the Sendafun library.
Start from a JSX download
Each icon detail page offers SVG, PNG and JSX. The JSX version is generated with SVGR, so it keeps the same geometry as the original SVG and exposes the standard SVG attributes as React props:
import ZapIcon from "./ZapIcon";
export function EmptyState() {
return (
<div className="empty-state">
<ZapIcon width={48} height={48} stroke="currentColor" />
<p>No results yet</p>
</div>
);
}
The generated component starts with a PascalCase name derived from the icon id, so `zap` becomes `ZapIcon` and `chat-bubble` becomes `ChatBubble`. That naming makes imports readable and avoids collisions with DOM elements.
Let color inherit from CSS
The most maintainable pattern is to keep the icon neutral in the component and color it with `currentColor`. Then one CSS class controls every icon in a button, alert or empty state:
const Wrapper = ({ children }) => (
<button className="cta cta--primary">
<ArrowRightIcon aria-hidden="true" />
{children}
</button>
);
.cta--primary svg {
color: #fff;
stroke-width: 2;
}
If you need per-use color instead, pass a `color` prop. Because the doodle style is stroke-based, `stroke="currentColor"` matters more than fill; changing the stroke color is what recolors the hand-drawn linework.
Standardize size with a small wrapper
Icon components without a size prop inherit the surrounding font size, which is useful in text but unpredictable in a grid. A thin wrapper fixes the contract:
function Icon({ name, size = 24, ...props }) {
const Component = ICONS[name];
if (!Component) return null;
return <Component width={size} height={size} {...props} />;
}
Using `width` and `height` instead of `font-size` keeps icon layout stable across browsers and screen readers. Pair the wrapper with a named map if you select icons dynamically.
Keep tree-shaking intact
Two mistakes commonly inflate bundle size. First, importing an index file that eagerly re-exports every icon. Second, loading a runtime map that builds all components before the page needs them. Both defeat tree-shaking.
- Import each icon directly: `import { Zap } from "icons/zap"` or a per-file default export.
- Use one icon per file so bundlers can drop unused files.
- Avoid `import * as Icons`; it tells the bundler everything may be used.
- If you really need a dynamic map, register only the icons you use.
With per-file exports, only the icons referenced by your app appear in the production bundle. A 24-pixel icon component weighs a few hundred bytes, so even a screen with dozens of icons stays light.
Add TypeScript without maintenance
The JSX downloads accept normal SVG props, so a generated `.tsx` file can declare its props once:
import type { SVGProps } from "react";
export default function ZapIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg viewBox="0 0 24 24" {...props}>
<path d="M13 2L4.5 13.5H11L9.5 22L19 10.5H12.5L13 2z" />
</svg>
);
}
That single type covers width, height, stroke, fill, `aria-hidden` and every other SVG attribute. If you want stricter control, narrow the props to `{ size?: number; stroke?: string; className?: string }` and map them internally.
Accessibility defaults
Decorative icons should be hidden from assistive technology with `aria-hidden="true"`. Icons that carry meaning, such as a warning in an inline alert, should have an accessible label instead:
<AlertTriangleIcon role="img" aria-label="Connection lost" />
Do not rely on the SVG title alone; `aria-label` on the component is explicit and works when the SVG is rendered through a component boundary.
Batch download when you need many
For a handful of icons, individual JSX downloads are fast. For a full design system, Solo Pro batch export produces a ZIP of up to 30 JSX components per export, which is the practical path when you are building a button set or navigation kit. Full-library download is not available for any plan, which also keeps generated icon packs from turning into accidental distribution vectors.