HTML to JSX: How to Convert Markup React Actually Accepts

⚛️ React workflows⏱️ 7 min readFree tool included

Every React developer pastes HTML into a component on day two, and every one of them hits the same wall: JSX looks like HTML but isn't. The class attribute warns, the style string throws, the <img> tag that never closes becomes a syntax error. The good news is that the distance between HTML and JSX is almost entirely mechanical — a fixed list of renames and rewrites — which is exactly the kind of work a converter does perfectly. Here's the full list, why React made these choices, and the boundary where automation stops.

Advertisement

Why can't React just accept HTML?

Because JSX compiles to function calls, not to markup. When you write <div className="hero">, Babel turns it into React.createElement('div', {className: 'hero'}) — a JavaScript object describing the element. Attribute names in JSX become keys of a props object, and since they eventually set DOM properties (not attributes), React chose the DOM property names: element.className, element.tabIndex, element.htmlFor. HTML named them differently twenty years earlier, and the mismatch is permanent now.

What actually changes: the complete list

HTMLJSXReason
classclassNameDOM property name
forhtmlForfor is reserved in JS
tabindex, readonly, maxlength, autocompletetabIndex, readOnly, maxLength, autoCompletecamelCase properties
colspan, rowspan, srcsetcolSpan, rowSpan, srcSetcamelCase properties
style="color:red"style={{color: 'red'}}style is an object in React
<br>, <img>, <input> unclosedself-closed <br />JSX demands closed tags
onclick="fn()"onClick={fn}events are props, functions
value on an inputdefaultValue (usually)uncontrolled default

Two of those deserve a beat. Style strings: the object's keys camel-case too, so font-size becomes fontSize, and values stay strings ('red'). value on inputs: a plain value makes the input controlled — React now expects you to hold that state; if you just pasted markup, defaultValue is the behavior the HTML had.

The SVG trap: why your icon loses its stroke

SVG adds a second rename list on top. React's SVG attributes are camelCase like everything else, so stroke-width, stroke-linecap, fill-rule, clip-path, and xlink:href must become strokeWidth, strokeLinecap, fillRule, clipPath, and xlinkHref. Paste an icon unchanged and nothing crashes — you just get a console full of "Invalid DOM property" warnings and an icon with no stroke, which is somehow more annoying than a crash. The exceptions prove the rule: aria-* and data-* keep their hyphens because React passes them through verbatim, and viewBox already is camelCase.

From markup to component

Converting markup is half the job; the other half is wrapping it as a component the way icon libraries expect. The svgr toolchain (what powers most design-system pipelines) turns an SVG file into:

You can reproduce that output per-icon in a browser converter (the one below has an SVG tab that does exactly this), or at build time with svgr if every icon in a folder needs it. The pattern to internalize: the component owns the geometry, the caller owns the presentation.

What no converter can do

Three things stay manual, always:

  1. Event handlers. onclick="doThing()" is a string evaluated at click time; React wants onClick={doThing} — a function reference from your scope. A converter can rename the attribute; only you can supply the function.
  2. Scripts and stylesheets. <script> and <link> don't map to React at all — move them into the app's build or effect logic.
  3. Template syntax. Mustaches, ERB tags, and PHP come through as literal text; strip them before converting or you'll be hunting braces in JSX.

Everything else — the renames, the style objects, the tag closing, the SVG camelCase, the wrapper — is mechanical, and mechanical work belongs to machines.

Convert HTML or SVG to JSX, free

Runs the open-source react-magic converter in your browser: attribute fixes, SVG renames, component wrappers, TypeScript. No upload.

Open the HTML to JSX Converter

Which engine should I trust?

The one with provenance. React's own HTML→JSX playground ran on an open-source converter (htmltojsx, BSD-licensed, from the react-magic project) for years, and that engine is still the most battle-tested implementation of the rename list. The ToolAspect converter runs it locally in your tab — no upload, which matters if the markup is proprietary. For bulk pipelines, wire svgr into your build; for one-off pastes while coding, a browser tab beats a terminal round-trip.

Adjacent conversions, while you're here: turning a web page into Markdown for docs, cleaning up icons in an SVG editor, or rasterizing them for non-React contexts with SVG to PNG.

Advertisement