HTML to JSX

className, htmlFor, object styles, comments, and void tags. Great for pasting HTML templates into React components.

{{ t("htmlToJsxHint") }}

{{ htmlToJsx.message }}

Overview

React was created by Jordan Walke at Facebook in 2011, presented internally as FaxJS, and released to the public at JSConf in May 2013. The community's initial reaction was one of skepticism, and in many cases, open rejection. JSX — the syntax that mixes HTML inside JavaScript — felt like a heresy. Separating HTML, CSS, and JavaScript into different files had been a near-sacred principle of web development since the 2000s. Putting markup inside JavaScript functions felt like a step backward against decades of best practices.

The reversal was quick. After a few months with React in production, most developers concluded that separating HTML from JS was a separation of technologies — not of actual concerns. A button component has markup, style, and behavior, and having all of it in a single component file made the code easier to maintain than three separate files kept in sync. JSX won the pragmatic debate. Today it is rare to find a React project without JSX as the default, and the format has influenced Solid.js, Preact, Qwik, and other frameworks.

The converter on this page solves the practical problem of migrating existing HTML templates to JSX. The changes are systematic: `class` becomes `className` (because `class` is a reserved keyword in JavaScript), `for` in labels becomes `htmlFor`, multi-word attributes like `tabindex` and `maxlength` become camelCase (`tabIndex`, `maxLength`), inline styles go from a string to a JavaScript object, HTML comments become JSX expressions, and void tags like `<img>` get explicit self-closing `<img />` because JSX follows well-formed XML rules.

Technical deep dive

Why JSX felt like a heresy and why it won anyway

  • Before JSX, building UIs with pure React required nested calls to `React.createElement('div', {className: 'container'}, React.createElement('h1', null, 'Title'))`. Functionally correct, but completely unreadable for anything beyond a trivial component. JSX was created as syntactic sugar that Babel compiles into those calls — but the DX (developer experience) is radically different.
  • Vue, Angular, and Svelte took different paths: Vue uses HTML-like templates with directives (`v-if`, `v-for`), Angular uses templates with its own syntax, and Svelte compiles to plain JavaScript. JSX is the approach closest to 'pure JavaScript with a syntactic extension' — which both attracts JS developers and unsettles designers accustomed to plain HTML.
  • The reframing of 'separation of concerns' was the argument that turned the debate. The original principle (HTML/CSS/JS in separate files) separated technologies, not product concerns. A modal component has logic, style, and markup that change together — separating them into different files only creates long-distance coupling.
  • JSX is not exclusive to React. Solid.js uses JSX but compiles differently (no Virtual DOM). Preact is a React alternative with compatible JSX at 3 KB in size. Qwik uses JSX with state serialization for partial hydration. Stencil.js uses JSX for native web components.
  • Babel transforms JSX to JavaScript before any execution. The modern React compiler (React 17+) no longer requires an explicit React import in every JSX file — the transform is automatic. TypeScript also natively supports JSX with `.tsx` files.

The HTML → JSX conversion rules: a systematic mapping

  • `class` → `className`: `class` is a reserved keyword in JavaScript used to define ES6 classes. In JSX, the corresponding attribute is `className`. Forgetting this conversion is the most common mistake when migrating templates.
  • `for` → `htmlFor`: The `for` attribute in `<label>` is also a reserved word (JavaScript's `for` loop). In JSX it is `htmlFor`. Similarly, `<input type="text">` without problematic attributes does not change.
  • camelCase attributes: `tabindex` → `tabIndex`, `maxlength` → `maxLength`, `readonly` → `readOnly`, `autofocus` → `autoFocus`, `crossorigin` → `crossOrigin`, `enctype` → `encType`. The rule: if the HTML attribute has a hyphen or is a multi-word attribute with inconsistent casing, JSX uses camelCase.
  • Inline styles: `style="color: red; font-size: 16px"` → `style={{ color: 'red', fontSize: '16px' }}`. CSS properties with hyphens become camelCase. Values are strings (except unitless numbers, like `zIndex: 10`). Note the double braces: the outer `{}` is JSX interpolation, the inner `{}` is the JavaScript object.
  • Void tags with explicit closing: `<img>`, `<input>`, `<br>`, `<hr>`, `<meta>`, `<link>` in HTML5 do not need closing. In JSX, which follows well-formed XML rules, all tags must be closed: `<img />`, `<input />`, `<br />`. Without this, Babel throws a parse error.

Code Snippets

HTML → JSX conversion reference
// ATTRIBUTES: HTML → JSX
// class="container"         → className="container"
// for="input-id"            → htmlFor="input-id"
// tabindex="0"              → tabIndex={0}
// maxlength="100"           → maxLength={100}
// readonly                  → readOnly
// autofocus                 → autoFocus

// INLINE STYLES:
// style="color:red"         → style={{ color: 'red' }}
// style="font-size:16px"    → style={{ fontSize: '16px' }}
// style="background-color"  → style={{ backgroundColor: '...' }}

// COMMENTS:
// <!-- HTML comment -->     → {/* JSX comment */}

// VOID TAGS:
// <img src="x.png">         → <img src="x.png" />
// <input type="text">       → <input type="text" />
// <br>                      → <br />
Bootstrap card → React component
// Original HTML (Bootstrap card)
// <div class="card" style="width: 18rem;">
//   <img src="img.jpg" class="card-img-top" alt="Description">
//   <div class="card-body">
//     <h5 class="card-title">Card Title</h5>
//     <p class="card-text">Descriptive text.</p>
//     <a href="#" class="btn btn-primary">Action</a>
//   </div>
// </div>

function Card({ image, alt, title, text, href }) {
  return (
    <div className="card" style={{ width: '18rem' }}>
      <img src={image} className="card-img-top" alt={alt} />
      <div className="card-body">
        <h5 className="card-title">{title}</h5>
        <p className="card-text">{text}</p>
        <a href={href} className="btn btn-primary">
          Action
        </a>
      </div>
    </div>
  );
}

Sample

<div class="box" tabindex="0">
  <img src="/a.png" alt="">
  <!-- ok -->
</div>

FAQ

What is this tool for?

It runs fully in your browser: useful to validate, format, or convert data in everyday development.

Are my inputs sent to a server?

Processing happens locally with JavaScript. We do not store what you paste into the text areas.

Can I use this for real production data?

Use at your own risk. For secrets (passwords, tokens), prefer controlled environments and your company policies. And always review the generated contents. Never trust blindly things you see on the internet.