HTML to JSX Converter

Paste HTML from a template, a design tool or a CodePen and get a React component that renders the same markup. The HTML is converted on your device and never uploaded.

HTML → JSX

Input

Settings

History

Load from URL

Why HTML does not paste straight into React

JSX looks like HTML but follows JavaScript rules, so markup copied from a Bootstrap example, an email template, an icon set or a designer’s export breaks in a dozen small ways: class is a reserved word, style must be an object, every tag must be closed, and attribute names are camelCase. Fixing them by hand is tedious and error-prone. This converter rewrites the markup into a component — export default function Component() returning the JSX — formatted with Prettier, ready to rename and drop into your project.

What gets rewritten

  • Attribute names: class becomes className, for becomes htmlFor, and HTML attributes React spells in camelCase are renamed — tabindex → tabIndex, maxlength → maxLength, readonly → readOnly, colspan → colSpan, autofocus → autoFocus. data-* and aria-* attributes are kept as written.
  • Inline styles become style objects: style="margin-top: 8px; background-color: #fff" turns into style={{ marginTop: '8px', backgroundColor: '#fff' }}. Vendor prefixes follow React’s rules (-webkit- → Webkit…, -ms- → ms…), and CSS custom properties keep their --name.
  • Void elements such as <br>, <img> and <input> are self-closed, and empty elements become <div />.
  • Boolean attributes like required, disabled or autofocus are written bare.
  • Comments become JSX comments: {/* note */}.
  • Text has its whitespace collapsed as a browser would display it; { and } are escaped, while entities such as &amp; are kept. Inside <pre>, text is emitted as string literals so every space and line break survives.
  • Several top-level elements are wrapped in a fragment <>…</>, since a component returns a single root.

Malformed HTML is parsed the way a browser would: implied end tags for <p>, <li> and table cells are added, and stray closing tags are ignored.

Events, forms, SVG and scripts

Event handlers: onclick="open()" becomes onClick={() => { … open(); }} with a TODO comment, because inline handler strings run in a different scope than React handlers. If the code uses this, the comment reminds you to use event.currentTarget; if it is not valid JavaScript, it is kept as a comment with a warning.

Form fields stay uncontrolled: value on inputs, selects and textareas becomes defaultValue, checked becomes defaultChecked, and <textarea> content moves into defaultValue. Otherwise React would render them read-only. Note that checked="false" still means checked in HTML, so it stays on, with an info message.

SVG keeps its case-sensitive names (viewBox), and hyphenated presentation attributes are camelCased: stroke-width → strokeWidth, xlink:href → xlinkHref. <script> and <style> bodies are passed through dangerouslySetInnerHTML so their content is not mangled.

Limits

The doctype is dropped, !important cannot be expressed in React inline styles and is removed with a warning, and attributes whose names are not valid in JSX are passed through an object spread. The converter does not create state, props or separate components, and <option selected> is kept as is, though React prefers defaultValue on the <select>. Check the output with the JavaScript formatter or tidy the source first with the HTML formatter. Nothing leaves your browser.

Examples

Card with styles and a click handler

class, for and tabindex are renamed, the style string becomes an object, void tags are self-closed and the comment becomes a JSX comment.

Input
<div class="card" style="margin-top: 8px; background-color: #fff" onclick="openCard()">
  <label for="email">Email</label>
  <input id="email" type="email" required autofocus>
  <br>
  <img src="/logo.png" alt="Logo">
  <!-- note -->
  <p tabindex="0">Hi &amp; welcome</p>
</div>
Output
export default function Component() {
  return (
    <div
      className="card"
      style={{ marginTop: '8px', backgroundColor: '#fff' }}
      onClick={() => {
        // TODO: port this inline onclick handler
        openCard();
      }}
    >
      <label htmlFor="email">Email</label>
      <input id="email" type="email" required autoFocus />
      <br />
      <img src="/logo.png" alt="Logo" />
      {/* note */} <p tabIndex="0">Hi &amp; welcome</p>
    </div>
  );
}
Open this example in the tool

Inline SVG icon

viewBox keeps its case, the hyphenated stroke attributes become strokeWidth, strokeLinecap and strokeLinejoin, and empty shapes self-close.

Input
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  <circle cx="11" cy="11" r="8"></circle>
  <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
Output
export default function Component() {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      strokeWidth="2"
      strokeLinecap="round"
      strokeLinejoin="round"
    >
      <circle cx="11" cy="11" r="8" />
      <line x1="21" y1="21" x2="16.65" y2="16.65" />
    </svg>
  );
}
Open this example in the tool

Form fragment with values

value and checked become defaultValue and defaultChecked, and the textarea content moves into defaultValue, keeping the fields editable in React.

Input
<form>
  <input type="text" name="q" value="keyboard">
  <input type="checkbox" name="instock" checked>
  <textarea name="note">Gift wrap, please</textarea>
  <button type="submit" disabled>Search</button>
</form>
Output
export default function Component() {
  return (
    <form>
      <input type="text" name="q" defaultValue="keyboard" />
      <input type="checkbox" name="instock" defaultChecked />
      <textarea name="note" defaultValue={'Gift wrap, please'} />
      <button type="submit" disabled>
        Search
      </button>
    </form>
  );
}
Open this example in the tool

Common errors and how to fix them

ErrorCauseFix
The onclick handler is not valid JavaScript; it is kept as a commentA warning: the inline handler string contains a syntax error or a template placeholder.Rewrite the handler as a function in your component and reference it with onClick={handleClick}.
React inline styles cannot be !important; the flag is droppedA warning: an inline style uses !important, which React style objects cannot express.Move that rule into a CSS class if the priority matters.
The tag <my:widget> is not a valid JSX element name; it is written as <mywidget>A warning: the markup uses a tag name with characters JSX does not allow.Check the renamed element and replace it with the component or element you intended.
The doctype is not part of JSX and was droppedAn info note: a full HTML document was pasted, including <!DOCTYPE html>.Usually paste only the body content; html, head and body tags rarely belong in a component.

Frequently asked questions

Why does class become className?

class is a reserved word in JavaScript, so React uses className for the HTML class attribute. Similarly, for becomes htmlFor.

Why are value and checked turned into defaultValue and defaultChecked?

In React, value and checked make an input controlled, which means it cannot be typed into without an onChange handler. The default props keep the field behaving like the original HTML.

Does it work for Preact, Solid or Next.js?

The output is standard React JSX, so it works in Next.js and Preact (with compat). Solid accepts class and lower-case events, so some renames are unnecessary there but harmless.

What happens to inline event handlers?

They become arrow functions containing the original code and a TODO comment. Review them, since this and the global scope differ from inline HTML handlers.

Is my HTML uploaded?

No. Parsing, rewriting and formatting all happen in your browser.

Related tools