When you paste HTML into a React component, nothing works. class becomes an error, inline styles break, and void elements missing their closing slash cause parse failures. Converting HTML to JSX is a mechanical but error-prone process — this guide explains every rule so you can do it confidently, or skip the manual work entirely with our HTML to JSX converter.
Why HTML and JSX Are Different
JSX looks like HTML but compiles to React.createElement() calls. Because it lives inside JavaScript, several HTML attributes clash with reserved keywords or follow JavaScript naming conventions instead of HTML conventions.
The core differences fall into five categories:
- Reserved keyword renames —
class→className,for→htmlFor - camelCase attributes —
onclick→onClick,tabindex→tabIndex - Inline style objects —
style="color:red"→style={{ color: 'red' }} - Self-closing void elements —
<br>→<br />,<img src="...">→<img src="..." /> - Comment syntax —
<!-- ... -->→{/* ... */}
Attribute Reference Table
| HTML | JSX | Reason |
|---|---|---|
class="btn" | className="btn" | class is a JS reserved word |
for="email" | htmlFor="email" | for is a JS reserved word |
onclick="fn()" | onClick={fn} | JS event naming convention |
onchange={...} | onChange={...} | camelCase |
tabindex="0" | tabIndex={0} | camelCase |
readonly | readOnly | camelCase |
maxlength="10" | maxLength={10} | camelCase |
colspan="2" | colSpan={2} | camelCase |
rowspan="3" | rowSpan={3} | camelCase |
crossorigin | crossOrigin | camelCase |
contenteditable | contentEditable | camelCase |
<br> | <br /> | JSX requires explicit close |
<img src="..."> | <img src="..." /> | JSX requires explicit close |
<input type="text"> | <input type="text" /> | JSX requires explicit close |
<!-- comment --> | {/* comment */} | JS block comment in expression |
Inline Styles
This is the most common source of confusion. HTML inline styles are strings; JSX inline styles are JavaScript objects.
<!-- HTML -->
<div style="color: red; font-size: 14px; background-color: #fff;"></div>
// JSX
<div style={{ color: 'red', fontSize: '14px', backgroundColor: '#fff' }}></div>
Three rules apply:
- The value is an object, not a string (hence double braces — one for JSX expression, one for the object literal).
- Property names are camelCase (
font-size→fontSize,background-color→backgroundColor). - Unitless numbers are fine for
opacity,zIndex, and similar properties; pixel values need a string ('14px').
Self-Closing Void Elements
In HTML5, void elements like <br>, <hr>, <img>, <input>, <link>, and <meta> do not need a closing tag. In JSX they must self-close:
<!-- HTML — valid -->
<img src="photo.jpg" alt="Photo">
<br>
<input type="text" name="email">
<hr>
// JSX — required
<img src="photo.jpg" alt="Photo" />
<br />
<input type="text" name="email" />
<hr />
Comments
HTML comments are stripped by the parser and never appear in the DOM. JSX comments must be wrapped in a JavaScript expression:
<!-- This is an HTML comment -->
<p>Content</p>
{/* This is a JSX comment */}
<p>Content</p>
Common Migration Scenarios
Migrating a Static HTML Template into a React Component
Start with a complete HTML document and strip the outer structure:
<!-- HTML -->
<!DOCTYPE html>
<html>
<head>
<title>Dashboard</title>
</head>
<body>
<div class="container">
<header class="header">
<nav>
<a href="/" class="logo">Brand</a>
<ul class="nav-list">
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>
</div>
</body>
</html>
// React component
export function Layout() {
return (
<div className="container">
<header className="header">
<nav>
<a href="/" className="logo">Brand</a>
<ul className="nav-list">
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>
</div>
);
}
Copying from Figma or Webflow
Design tools typically export class-based HTML. The conversion is straightforward — rename class to className throughout, camelCase any event attributes, and self-close void elements.
Converting Email HTML for React Email
Email HTML is often full of table-based layouts and inline styles. The inline style conversion is the main task: every style="..." string becomes a style={{ ... }} object. Tools like react-email and jsx-email accept standard JSX, so a converted template works directly.
Gotchas
Boolean attributes — HTML uses disabled, checked, selected as bare attributes. JSX passes them as disabled={true} or simply disabled (shorthand). The converter handles both forms.
data-* and aria-* attributes — these stay hyphenated in JSX. data-testid="btn" and aria-label="Close" do not get camelCased.
SVG attributes — SVG has its own attribute naming quirks. viewBox is camelCase in JSX, stroke-width becomes strokeWidth. If you’re converting SVG specifically, our SVG to JSX converter handles these rules correctly.
Numeric attribute values — in HTML, tabindex="0" is a string. In JSX, prefer the number form: tabIndex={0}. Most converters handle this automatically.
Convert HTML to JSX Online
ZeroTool’s HTML to JSX converter applies all of the rules above instantly in your browser. Paste any HTML snippet and get valid JSX output — no server, no signup, no data sent anywhere.