HTML to JSX Converter

Convert HTML to React JSX instantly in your browser. Handles className, htmlFor, inline style objects, camelCase events, self-closing tags, and more. Free, no signup.

  • Runs in your browser
  • Your data never leaves your browser
  • Free · No Sign-Up
Copy the complete displayed JSX. Empty output is not copied.

Paste HTML. Conversion runs 200 ms after you stop typing. With focus in this tool, Ctrl/⌘+L clears both panels and the status.
The output is JSX markup without a component wrapper. Styles that cannot be preserved stop conversion, clear the output and disable Copy; the status gives the position and reason.
Examples, details and FAQ Worked examples, how it compares with other tools, and answers to common questions.

Conversion Rules

HTMLJSX
class="..."className="..."
for="..."htmlFor="..."
style="color:red"style={{ "color": "red" }}
onclick="save()"onClick={(event) => { save() }}
ondblclick="..." / onmouseover="..."onDoubleClick={...} / onMouseOver={...}
tabindextabIndex
readonlyreadOnly
maxlengthmaxLength
colspancolSpan
rowspanrowSpan
crossorigincrossOrigin
contenteditablecontentEditable
autocomplete, novalidate, srcset, datetime, spellcheckautoComplete, noValidate, srcSet, dateTime, spellCheck
http-equiv, accept-charsethttpEquiv, acceptCharset
<br><br />
<img src="..."><img src="..." />
<input ...><input ... />
<!-- comment -->{/* comment */}
Text {name}, a < b&#123;name&#125;, a &lt; b

Common Use Cases

  • Migrating static HTML templates into React components
  • Copying HTML snippets from design tools (Figma, Webflow) into JSX files
  • Learning the differences between HTML and JSX attribute naming
  • Converting email HTML templates for use in React email libraries
  • Quickly adapting third-party HTML widgets for React projects

Example

Input:

<label for="email" class="lbl">Email</label>
<input id="email" type="email" required>
<div style="background-image: url('a.png'); font-size:14px">x</div>
<!-- footer -->

Output:

<label htmlFor="email" className="lbl">Email</label>
<input id="email" type="email" required />
<div style={{ "backgroundImage": "url('a.png')", "fontSize": "14px" }}>x</div>
{/* footer */}

A login form shows several rules at once. The input

<form action="/login" method="post" novalidate>
  <label for="user" class="field-label">Username</label>
  <input id="user" name="user" autocomplete="username" maxlength="32" autofocus>
  <button type="submit" onclick="track(&quot;login&quot;)">Sign in</button>
</form>

becomes

<form action="/login" method="post" noValidate>
  <label htmlFor="user" className="field-label">Username</label>
  <input id="user" name="user" autoComplete="username" maxLength="32" autoFocus />
  <button type="submit" onClick={(event) => { track("login") }}>Sign in</button>
</form>

Six attribute names change (novalidate, for, class, autocomplete, maxlength and the boolean autofocus), the void <input> gets />, and the onclick string becomes an arrow function with &quot; decoded back to ". Without the rename, React DOM prints a development warning such as “Invalid DOM property autocomplete. Did you mean autoComplete?”, so pasting unconverted HTML usually works but floods the console. The attribute names come from the same table React uses to suggest them (possibleStandardNames.js in react-dom).

data-* and aria-* attributes stay hyphenated, as React expects. Numeric attributes stay strings, such as colSpan="2"; React accepts them.

What You Still Change by Hand

Converter outputProblem in ReactChange to
onClick={(event) => { save(); return false; }}The code runs in an arrow function: this is not the element, and return false does not cancel the default actionevent.currentTarget, event.preventDefault(), or pass the function: onClick={save}
<input type="checkbox" checked>A checked prop without onChange makes a read-only fielddefaultChecked
<option selected>React sets the selection on <select><select defaultValue="...">
<textarea>hi</textarea><textarea> does not accept children<textarea defaultValue="hi" />
style="color:red !important" (no output)Refused: a React style object cannot keep !important, so the output is clearedMove the rule to a class in your CSS, or drop !important
Two or more top-level elementsA component returns one elementWrap them in <>...</>
<path stroke-width="2">SVG attributes are not convertedUse SVG to JSX

Limits

  • Attribute values are copied as written. Only attribute names change, so href="/search?for=1&class=2" stays the same. This is what the htmltojsx converter does too. Two values change: style becomes an object, and the code of an event handler goes into (event) => { ... }, because React needs a function, not a string. Entities such as &quot; in that code are decoded. An on* attribute that is not a DOM event, such as onfoo on a custom element, is kept as written.
  • Text keeps its characters. In text between tags, {, }, < and > are written as &#123;, &#125;, &lt; and &gt;, which JSX turns back into the same characters, so <p>{name}</p> renders the text {name} instead of reading a variable. Other text is copied as is, and a full document keeps its <!DOCTYPE>, <html>, <head>, and <body>. Paste only the part that goes into the component.
  • Styles that cannot be kept are refused. !important, a property written twice (color:red;color:blue, often a CSS fallback), CSS comments, escaped property names and unclosed quotes or brackets stop the conversion. The output is cleared, Copy is disabled and the status line names the character position in the style, for example “Cannot convert style at position 11: !important cannot be preserved in a React style object”. Other values, including url(data:…;base64,…) and custom properties like --accent, are copied character for character as JSON strings.
  • No component wrapper. The output is markup. Put it in the return (...) of your component.
  • No HTML parsing or repair. The converter rewrites opening tags it can match with a pattern; it does not build a DOM. Unclosed tags stay unclosed, and an attribute written twice (class="a" CLASS="b") becomes two className props, which JSX does not allow. Paste markup that already validates.
  • Comments. A */ inside an HTML comment is written as * /, so the JSX comment does not end early.

FAQ

What does an HTML to JSX converter do?

It transforms standard HTML markup into valid React JSX syntax. Key changes include renaming class to className, for to htmlFor, converting inline styles to JavaScript objects, renaming event attributes to React's camelCase names and wrapping their code in a function, and adding explicit self-closing slashes to void elements.

Why is className used instead of class in JSX?

JSX is compiled to JavaScript, and class is a reserved keyword in JavaScript. React uses className to avoid the collision. The converter handles this rename automatically.

How are inline styles converted?

HTML inline styles use a string: style="color: red; font-size: 14px". JSX requires an object, so the converter writes style={{ "color": "red", "fontSize": "14px" }}. It splits declarations only at top-level semicolons, so a semicolon inside quotes or url(data:…) stays in the value, keeps custom properties such as --accent as written, and copies each value exactly. A style that a React style object cannot keep (!important, the same property twice, a CSS comment, an unclosed quote or bracket) is refused: the output is cleared, Copy is disabled and the status line gives the character position.

What do HTML comments become in JSX?

HTML comments (<!-- text -->) are converted to JSX expression comments ({/* text */}), the only form React's JSX parser accepts.

Is anything sent to a server?

No. The conversion runs in your browser using JavaScript. No data is transmitted.