Skip to main content

HTML to JSX Converter

Convert HTML to valid React JSX instantly — class becomes className, for becomes htmlFor, inline styles turn into JSX object syntax, void elements self-close, and HTML comments convert to {/* ... */}. Free, 100% in-browser, no upload.

HTML input
JSX output

Event-handler attributes (onClick, onChange, etc.) are renamed but their values are kept as strings — replace them with function references in your component code. Inlineonclick="track(e)" handlers stay as string values after conversion; they will not execute as JS in JSX.

30+ Attribute Renames

class → className, for → htmlFor, tabindex → tabIndex, maxlength → maxLength, and 25+ more. Event handlers (onclick → onClick) and data-/aria- attributes handled correctly.

Style String → Object

Inline style="color: red; font-size: 16px" becomes style={{ color: 'red', fontSize: '16px' }} — kebab-case properties converted, numeric values kept as numbers.

Self-Closes Void Elements

<br>, <hr>, <img>, <input>, <meta>, <link>, and other void elements get the required JSX self-closing slash. HTML comments converted to JSX comment expressions.

100% Client-Side

Parser uses the browser's native DOMParser. No fetch, no upload, no signup. Works offline once the page loads.

HTML to JSX Converter: paste HTML, get React JSX

An HTML to JSX converter rewrites raw HTML into the JSX syntax React requires: class becomes className, for becomes htmlFor, inline style strings become JavaScript objects, void tags like <br> self-close, and <!-- --> comments become {/* */}. Paste a mockup, designer export, or AI-generated snippet and copy valid JSX back. It runs free, 100% in your browser, with no upload.

How to convert HTML to JSX

  1. Paste your HTML into the input panel, or load the built-in sample to see a full card pattern converted.
  2. Pick an indent of 2 or 4 spaces to match your project's formatter.
  3. Toggle "Wrap multiple roots in Fragment" if your snippet has more than one top-level element — JSX must return a single root.
  4. Read the converted JSX in the output panel; attributes, styles, void tags, and comments are already rewritten.
  5. Press Copy and paste straight into your component's return.
  6. Rewrite any inline event handler string (onClick="...") into a real function reference before you ship.

Why JSX differs from HTML

JSX is not HTML — it is syntactic sugar that compiles to JavaScript, so its attributes become keys of plain JS objects. That single fact drives every rename. As the official React "Rules of JSX" documentation puts it, "since class is a reserved word, in React you write className instead, named after the corresponding DOM property." The same page sets out three hard rules: return a single root element, close every tag, and camelCase most attributes. JavaScript identifiers cannot contain dashes or be reserved words, which is exactly why tabindextabIndex and forhtmlFor.

The one documented exception: aria-* and data-* attributes keep their dashes, written exactly as in HTML "for historical reasons." Self-closing also comes from a spec — the 15 void elements (area, base, br, col, embed, hr, img, input, keygen, link, meta, param, source, track, wbr) are defined by the WHATWG HTML void-element list, and JSX requires the trailing slash that HTML lets you omit. This tool parses your input with the browser's native DOMParser (lenient HTML mode, not strict XML), then walks the DOM tree to emit JSX — the same parser the browser uses, so what you see is what the browser saw.

Worked examples: HTML → JSX

Attribute renames

<label for="email" class="lbl">Email</label>

<label htmlFor="email" className="lbl">Email</label>

Void element + inline style

<img src="/a.jpg" style="border-radius: 8px">

<img src="/a.jpg" style={{ borderRadius: '8px' }} />

HTML comment

<!-- hero card -->

{/* hero card */}

Edge case · unitless vs unit values

The converter keeps unitless numeric style values as raw JS numbers but quotes everything else. So style="line-height: 1.5; font-size: 16px" becomes style={{ lineHeight: 1.5, fontSize: '16px' }}1.5 is a number, '16px' is a string. This matches how React treats numeric style values, but double-check unitless properties you did not expect to be numeric.

HTML → JSX attribute renames

HTMLJSXWhy
classclassNameReact reserves "class" for ES6 class declarations; JSX uses className.
forhtmlFor"for" is a reserved keyword in JavaScript loop syntax.
tabindextabIndexJSX uses camelCase for all multi-word HTML attributes.
readonlyreadOnlySame camelCase rule applies to boolean attributes.
maxlengthmaxLengthAlso affects minlength, colspan, rowspan, cellpadding, cellspacing.
onclickonClickAll event handlers convert from lowercase to onCamelCase form.
autocompleteautoCompleteAffects form input behavior hints sent to the user agent.
spellcheckspellCheckEditable element behavior toggle.
srcsetsrcSetResponsive image source set.
crossorigincrossOriginCORS request hint for img, script, link.
data-*, aria-*data-*, aria-*Custom data and ARIA attributes keep their dashed form (no rename).

The converter applies 40+ renames covering every commonly-used HTML attribute, plus the full on* event-handler family (onclick → onClick, onchange → onChange). data-* and aria-* keep their dashed form.

Inline style → JSX object syntax

HTMLJSX
style="color: red"style={{ color: 'red' }}
style="font-size: 16px; line-height: 1.5"style={{ fontSize: '16px', lineHeight: 1.5 }}
style="background-color: #f3f4f6"style={{ backgroundColor: '#f3f4f6' }}
style="padding: 8px 16px"style={{ padding: '8px 16px' }}
style="--theme: dark"style={{ '--theme': 'dark' }}

Note the double curly braces in JSX: the outer pair is JSX expression syntax, the inner pair is the object literal. Numeric values stay unquoted; strings get single quotes; CSS custom properties get quoted keys.

Five HTML-to-JSX bugs this tool prevents

1. Forgetting className

JSX silently renders class as an attribute on the DOM but React ignores it for component identity. Always use className.

2. Inline style as string

JSX requires an object: style={{ color: 'red' }}, not style="color: red". Watch the double curly braces.

3. Self-closing void elements

<br>, <hr>, <img>, <input>, <meta>, <link> must be self-closed in JSX: <br />, <img />.

4. Inline JS event values

onclick="track(e)" becomes onClick={track} — replace string with function reference; arrow functions if you need to pass args.

5. HTML comments inside JSX

<!-- comment --> is invalid JSX. Use {/* comment */} instead — note the curly braces.

The two things every HTML-to-JSX tool gets wrong

First, curly braces in text are not safe in JSX. A literal { or } in body text is read as the start of a JS expression, which throws. This converter escapes them as {"{"} and {"}"} so pasted content with braces (think template placeholders or code samples) still compiles. Most naive find-and-replace tools miss this and produce JSX that fails to build.

Second, because parsing uses DOMParser in lenient HTML mode, the browser applies its own tree-construction rules to your fragment. Paste a bare <td> or <li> with no surrounding table or list and the parser may drop or reparent it — the same thing your browser would do. If a tag silently vanishes from the output, wrap it in its valid parent (<tr>, <ul>) before converting.

Runs 100% in your browser

Your HTML never leaves your device. Parsing uses the browser's native DOMParser and the JSX is built with plain string concatenation — no fetch, no upload. I tested this against real designer exports, a 200-line landing-page card, deeply nested tables, mixed inline styles with custom properties, and snippets containing both HTML comments and stray curly braces. The output stayed instant and the brace-escaping held. Pasted HTML often carries proprietary designs or internal prototypes, so keeping it client-side is the point — verify in DevTools under the Network tab.

Frequently asked questions

Is this HTML to JSX converter free?

Yes — 100% free with no signup and no usage cap. Convert and copy as much HTML as you like. Every conversion runs locally in your browser, so there is nothing to bill for and nothing to upload.

Why does React use className instead of class?

Because class is a reserved word in JavaScript and JSX compiles to JS objects. React names the prop after the DOM property, className; for becomes htmlFor for the same reason.

Why must void elements self-close?

JSX requires every tag to be explicitly closed, so HTML's 15 void elements (<br>, <img>, <input>, etc.) become <br />, <img />. A missing slash is the most common parse error.

Is my HTML sent to a server?

No. Parsing uses the browser's built-in DOMParser and emission is pure string building — no fetch, no XHR, no analytics carrying your markup. Confirm it in DevTools under the Network tab.

Last updated: June 2, 2026 · Runs 100% in your browser — no uploads, nothing leaves your device.

Need a different tool?

Browse all 89 free, in-browser tools — or tell us what we should build next.

Browse all tools