JSX Is Not HTML
This distinction matters more than it might seem. JSX is a syntax extension for JavaScript that looks like HTML but compiles to JavaScript function calls. <div className="box">Hello</div> becomes React.createElement("div", { className: "box" }, "Hello"). Understanding this helps explain why JSX rules exist — they are constraints of JavaScript, not of HTML.
The good news: most HTML converts to JSX with only minor changes. The bad news: those minor changes are inconsistent enough that they trip up developers moving between plain HTML templates and React components regularly.
The Full List of Differences
Attribute Name Changes
JSX attributes use camelCase JavaScript property names, not HTML attribute names. The most important:
class→className(reserved word in JavaScript)for→htmlFor(reserved word in JavaScript)tabindex→tabIndexreadonly→readOnlymaxlength→maxLengthautocomplete→autoCompletecontenteditable→contentEditablecrossorigin→crossOrigin
Self-Closing Tags
In HTML5, void elements (<br>, <img>, <input>, <hr>, <meta>, <link>) do not need closing tags. In JSX, every element must be closed, either with a closing tag or the self-closing shorthand:
// HTML
<br>
<img src="photo.jpg" alt="Photo">
<input type="text">
// JSX
<br />
<img src="photo.jpg" alt="Photo" />
<input type="text" />
Inline Styles
In HTML, style is a string: style="color: red; font-size: 14px". In JSX, style is an object with camelCase property names and string values (or unitless numbers for pixel values):
// HTML
<div style="color: red; font-size: 14px; background-color: #f0f0f0">
// JSX
<div style={{ color: 'red', fontSize: '14px', backgroundColor: '#f0f0f0' }}>
The double curly braces are not magic — the outer {} is JSX's "enter JavaScript expression" syntax, and the inner {} is a JavaScript object literal.
Event Handlers
HTML events use lowercase attribute names. JSX uses camelCase:
onclick→onClickonchange→onChangeonsubmit→onSubmitonkeydown→onKeyDownonmouseenter→onMouseEnter
In HTML, event handlers are strings: onclick="handleClick()". In JSX, they are function references: onClick={handleClick}. Do not call the function — pass a reference. onClick={handleClick()} calls the function immediately during render, which is almost never what you want.
JSX Comments
HTML comments (<!-- comment -->) do not work in JSX. Use JavaScript comments inside an expression block:
{/* This is a JSX comment */}
<div>{/* Another comment */}</div>
Single Root Element
JSX expressions must have exactly one root element. If you need to return multiple elements without a wrapper div, use a Fragment:
// This fails — two root elements
return (
<h1>Title</h1>
<p>Paragraph</p>
);
// Use Fragment — renders no extra DOM element
return (
<>
<h1>Title</h1>
<p>Paragraph</p>
</>
);
Boolean Attributes
In HTML, the presence of an attribute implies true: <input disabled>. In JSX, boolean attributes must be explicitly set:
<input disabled={true} />
// Or shorthand: JSX treats a bare attribute as true
<input disabled />
To set an attribute to false, pass the value explicitly: <input disabled={false} /> — which is the same as omitting the attribute entirely.
The dangerouslySetInnerHTML Warning
HTML's innerHTML equivalent in JSX is dangerouslySetInnerHTML — the name is intentional. Inserting raw HTML from user input without sanitization is a major XSS vulnerability. Only use this with content you fully control or that has been sanitized server-side:
<div dangerouslySetInnerHTML={{ __html: sanitizedHtmlString }} />
Automate the Conversion
Converting large amounts of HTML to JSX by hand is error-prone and tedious. Use PureFormatter's HTML to JSX converter to handle all attribute renaming, self-closing tag corrections, and style object conversions automatically. It handles edge cases like inline SVG, data attributes, and aria attributes correctly.