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 to JSX Converter workspace

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 use the HTML to JSX converter

  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.

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

Frequently asked questions

Why does React insist on className instead of class?

JSX compiles to JavaScript objects, and class is a reserved word in that language — so React names the prop after the matching DOM property, className. The same rule renames for to htmlFor on labels. The converter applies both renames automatically wherever they appear in your markup.

Which HTML features change beyond attribute renaming?

Void elements must self-close (br becomes <br />), inline style strings become object literals with camelCase keys, and HTML comments turn into JavaScript braces or are dropped. Every tag needs explicit closing because JSX has no implied-end-tag parsing.

Does the converted output include my event handlers?

No — attributes like onclick stay as plain string attributes, which React will warn about at runtime. The tool’s job is structural conversion; wire up handlers as real functions after pasting the JSX into your component file.

Is my markup uploaded during conversion?

No. Parsing uses the browser’s built-in DOMParser and emission is pure string building inside your tab; Toolk’s page analytics do not receive the markup you paste. Confirm it yourself in DevTools under the Network tab — nothing fires while converting.

Any tip before I paste the result into a component?

Run messy source through Toolk’s HTML Formatter (/tools/html-formatter) first — readable input makes the converted JSX far easier to review line by line, especially for large email-template or landing-page markup.

Need a different tool?

Browse all 99 browser-based tools (99 currently marked free), or tell us what useful utility we should build next.

Browse all tools