You paste an icon from Figma into a React component. The build fails, or worse, it renders with hairline strokes and no error. The culprit is one hyphen, and it appears in nearly every SVG a design tool exports.
Why SVG Attributes in React Need camelCase
Because JSX is JavaScript, and a hyphen means subtraction there.
JSX looks like HTML but compiles into JavaScript function calls. Every attribute becomes a property on an object.
JavaScript identifiers cannot contain hyphens. Write stroke-width and the parser sees stroke minus width — two variables and an operator.
So React uses strokeWidth. Same attribute, name that JavaScript can hold.
Our SVG to JSX converter rewrites all of them in one pass, if you would rather not do it by hand.
The stroke-width JSX Error and Its Cousins
Most of the time it does not throw — it warns. That is what makes it slow to find.
React passes attributes it does not recognise straight to the DOM. The element renders, the property never applies, and your icon looks subtly wrong.
Here is the full conversion table:
| SVG attribute | JSX property | Used for |
|---|---|---|
| stroke-width | strokeWidth | Outline thickness |
| stroke-linecap | strokeLinecap | Line end shape |
| stroke-linejoin | strokeLinejoin | Corner shape |
| stroke-dasharray | strokeDasharray | Dashed outlines |
| stroke-miterlimit | strokeMiterlimit | Sharp corner cutoff |
| fill-rule | fillRule | Interior fill algorithm |
| fill-opacity | fillOpacity | Fill transparency |
| clip-path | clipPath | Clipping region |
| clip-rule | clipRule | Clipping algorithm |
| stop-color | stopColor | Gradient stops |
| stop-opacity | stopOpacity | Gradient transparency |
| flood-opacity | floodOpacity | Filter flood effects |
| text-anchor | textAnchor | Text alignment |
| font-family | fontFamily | Text font |
| letter-spacing | letterSpacing | Character spacing |
| class | className | CSS classes |
The pattern is consistent: drop the hyphen, capitalise what followed it.
The clip-path Trap That Survives Conversion
Renaming the attribute is only half the job.
A clipPath value is usually a reference: url(#mask-a). That points at an id defined elsewhere in the same file.
Convert two icons that both export with id="mask-a", render both on one page, and you now have duplicate ids. One silently wins and the other icon clips wrong.
In our experience this is the hardest SVG bug to diagnose, because each icon works perfectly on its own. Rename ids to something unique per icon before shipping.
Three Attributes That Follow Different Rules
Not everything hyphenated becomes camelCase. Three exceptions worth knowing:
- data-* and aria-* keep their hyphens. React passes them straight through, so
data-testidandaria-labelstay as written. - Already-camelCase attributes such as
viewBox,preserveAspectRatioandgradientTransformstay exactly as they are. Lowercasing them breaks the element. - Namespaced attributes use a colon, not a hyphen.
xlink:hrefbecomesxlinkHref, andxml:spacebecomesxmlSpace.
That last group catches converters that only handle hyphens. If your icon references a symbol and stopped working, check the xlink attribute first.
Inline Styles Need an Object, Not a String
This one throws rather than warns.
// Breaks
style="fill: red; stroke-width: 2"
// Works
style={{ fill: 'red', strokeWidth: 2 }}
Double braces confuse people the first time. The outer pair means "JavaScript expression", the inner pair is the object itself.
Keys are camelCased here too, for the same reason as everywhere else.
Worth noting for comparison: Vue accepts the string exactly as your exporter wrote it, which is why the SVG to Vue component converter has far less to do.
Let TypeScript Catch It Instead
A wrong attribute becomes a compile error rather than a console warning.
That difference is larger than it sounds. Console warnings scroll past during development and nobody reads them before a deploy.
On an icon-heavy codebase we would use .tsx for this reason alone. Our SVG to TSX converter generates the typed props interface for you.
One honest caveat: TypeScript catches unknown attributes, not wrong values. A strokeWidth of the wrong number still type-checks.
Wrapping Up
SVG attributes in React need camelCase because JSX compiles to JavaScript and JavaScript identifiers cannot hold a hyphen. That is the whole rule.
Remember the exceptions: data and aria keep their hyphens, already-camelCase attributes stay untouched, and namespaced ones use a different pattern.
For the wider picture, see the seven ways to use SVG in React, or automate the whole thing with SVGR or an online converter.
