Published by Risetop · 8 min read
If you've ever copied an HTML snippet into a React component and watched your entire app crash with a cryptic error message, you're not alone. Converting HTML to JSX is one of the most common friction points for developers moving to React — or even for experienced React devs who need to migrate a static page into a component. The syntax looks nearly identical at a glance, but the differences run deep enough to cause real headaches if you don't know what to watch for.
This guide walks you through every rule, edge case, and gotcha you'll encounter when converting HTML to JSX. Whether you're doing it manually or using an automated converter, understanding the why behind each change will save you hours of debugging.
JSX is a syntax extension for JavaScript that lets you write markup inside your code. It looks like HTML — tags, attributes, nested elements — but it compiles down to React.createElement() calls at build time. Because JSX lives inside JavaScript, it has to follow JavaScript's rules, and that's where the divergence from standard HTML begins.
The core difference is philosophical: HTML is a document markup language designed to be forgiving. Browsers will silently fix your malformed HTML, close unclosed tags, and render just about anything you throw at them. JSX, on the other hand, is compiled into function calls, and the compiler is strict. If you write invalid JSX, your build fails — not at runtime, but at compile time. This strictness is actually a feature: it catches errors before your code reaches users.
Understanding this distinction is key. You're not just "tweaking" HTML — you're writing JavaScript that represents UI. Every JSX transformation rule exists because of this fundamental shift in execution context.
class with classNameThis is the single most common conversion. In HTML, class is a standard attribute for applying CSS classes. In JavaScript, class is a reserved keyword used for ES6 class declarations. Since JSX is JavaScript, using class as an attribute name would create a syntax conflict. React solves this by using className instead.
<!-- HTML -->
<div class="container">
{/* JSX */}
<div className="container">
Most modern build tools will warn you if you accidentally use class, but it won't always break immediately — it depends on your React version and whether you're using React 19+ with the new JSX transform. Still, always use className for consistency.
for with htmlForSimilarly, for is a reserved JavaScript keyword (used in for loops). The for attribute on <label> elements must become htmlFor in JSX.
<!-- HTML -->
<label for="email">Email</label>
{/* JSX */}
<label htmlFor="email">Email</label>
In HTML, self-closing tags like <br>, <img>, <input>, and <hr> don't require a closing slash. Browsers handle them fine either way. In JSX, every element must be explicitly closed — either with a closing tag or with a self-closing slash.
<!-- HTML -->
<img src="photo.jpg">
<input type="text">
<br>
{/* JSX */}
<img src="photo.jpg" />
<input type="text" />
<br />
HTML attributes use lowercase (or kebab-case for custom attributes), but JSX attribute names must be camelCase because they map to JavaScript property names. This affects several attributes beyond class and for.
<!-- HTML -->
<div tabindex="0" onclick="handler()">
<input maxlength="50" readonly>
<svg viewBox="0 0 100 100">
{/* JSX */}
<div tabIndex={0} onClick={handler}>
<input maxLength={50} readOnly />
<svg viewBox="0 0 100 100">
Note that SVG attributes like viewBox retain their camelCase form in JSX, which is actually more consistent with the SVG spec than HTML's treatment of them.
In HTML, the style attribute accepts a CSS string. In JSX, style accepts a JavaScript object where property names are camelCase CSS properties and values are strings or numbers.
<!-- HTML -->
<div style="background-color: blue; font-size: 16px; margin-top: 20px;">
{/* JSX */}
<div style={{ backgroundColor: 'blue', fontSize: '16px', marginTop: '20px' }}>
The double curly braces are not a typo. The outer braces are JSX expression delimiters (telling React "evaluate this as JavaScript"), and the inner braces define the object literal. This is one of the most visually confusing JSX patterns for beginners, but it becomes second nature quickly.
An HTML file can have multiple root-level elements. A JSX expression must return exactly one root. If you need to return multiple elements, wrap them in a parent <div> or use React Fragments (<>...</>) to avoid adding extra DOM nodes.
{/* Invalid JSX — two root elements */}
return (
<h1>Title</h1>
<p>Content</p>
);
/* Valid JSX — wrapped in Fragment */}
return (
<>
<h1>Title</h1>
<p>Content</p>
</>
);
Fragments are almost always preferable to wrapper divs because they don't add unnecessary nodes to the DOM tree, which keeps your CSS and JavaScript simpler.
In HTML, you'd use template literals or framework-specific syntax for dynamic values. In JSX, any JavaScript expression inside markup must be wrapped in curly braces.
{/* JSX */}
<h1>Hello, {userName}</h1>
<img src={imageUrl} alt={imageAlt} />
<div className={isActive ? 'active' : 'inactive'}>
This includes ternary expressions, function calls, variable references, and any other valid JavaScript expression. Just remember: no if/else statements inside JSX — use ternaries or extract the logic above the return.
HTML entities like , ©, and < work in JSX, but if you need to use a literal ampersand, you must write it as & or use the Unicode character directly. More importantly, JSX doesn't support HTML comments — you must use JavaScript comments inside curly braces or block comments.
{/* JSX comment */}
{/* A valid comment in JSX */}
{/* Multi-line comment:
This works too */}
<div>{/* inline comment */}</div>
In HTML, event handlers are strings that reference global functions. In JSX, event handlers are function references (or inline arrow functions). The naming convention also changes to camelCase.
<!-- HTML -->
<button onclick="handleSubmit()">Submit</button>
{/* JSX */}
<button onClick={handleSubmit}>Submit</button>
Avoid inline arrow functions in render for frequently-called events like onChange on text inputs — they create a new function reference on every render, which can hurt performance. Pass the function reference directly instead.
In HTML, boolean attributes like disabled, checked, and required are present-or-absent — their value doesn't matter. In JSX, you should explicitly set them to true or pass the boolean expression.
{/* JSX */}
<input disabled={true} />
<input disabled /> {/* shorthand for true */}
<input disabled={false} /> {/* explicitly enabled */}
Custom data-* attributes pass through to the DOM as-is in kebab-case, so data-id="123" works fine in JSX without conversion.
HTML doesn't have a native way to conditionally render content (you'd use JavaScript to manipulate the DOM). JSX supports several patterns:
{/* Ternary for either/or */}
{isLoggedIn ? <Dashboard /> : <LoginForm />}
{/* Logical AND for show/hide */}
{showBanner && <Banner message="Welcome!" />}
{/* Immediately invoked function for complex logic */}
{(() => {
if (status === 'loading') return <Spinner />
if (status === 'error') return <ErrorMessage />
return <Content data={data} />
})()}
Manually converting HTML to JSX is educational, but for large blocks of markup — especially when migrating existing websites — an automated converter saves significant time. Here's what to look for in a converter tool:
Accuracy across edge cases. A good converter handles all eight rules above, plus edge cases like SVG attributes, ARIA properties, and custom data attributes. It shouldn't break valid HTML or lose information during conversion.
Preservation of formatting. The output should maintain readable indentation and structure. A converter that produces a single-line mess is worse than useless — you'll spend more time reformatting than you would have spent converting manually.
Component extraction hints. Advanced converters can identify reusable patterns and suggest component boundaries, though this is still an emerging capability. At minimum, the tool should produce valid JSX that you can paste directly into a React component.
When using any converter, always review the output. Automated tools handle the mechanical transformations reliably, but they can't make architectural decisions about state management, prop drilling, or component structure. The converter gives you valid JSX; you still need to make it good React code.
Beyond syntax conversion, writing clean JSX means following patterns that make your components maintainable and performant:
Extract repeated logic. If you find yourself writing the same conditional rendering or style object in multiple places, extract it into a variable or a helper function above the return statement.
Avoid deeply nested ternaries. More than one level of ternary nesting makes code hard to read. Use early returns, helper functions, or a dedicated component to simplify the rendering logic.
Use semantic HTML even in JSX. Just because you're writing React doesn't mean you should replace every <button> with a <div onClick>. Semantic elements improve accessibility and SEO, and they work perfectly in JSX.
Keep JSX expressions simple. If a curly brace expression spans more than one line, consider extracting it into a variable. Your render method should read like a template, not like business logic.
No. React components must return valid JSX, not raw HTML. If you have an HTML string that needs to be rendered, use the dangerouslySetInnerHTML prop as a last resort — but be aware that this bypasses React's XSS protection and should never be used with user-provided content.
Yes, JSX supports all standard HTML5 elements. Custom elements (web components) also work, though some older React versions had quirks with custom element attribute passing. React 19 has improved web component support significantly.
This usually means you've left an HTML-specific syntax that JSX doesn't accept. Common causes include: using class instead of className, unclosed self-closing tags, HTML comments (<!-- -->) instead of JSX comments, or attribute values without quotes in certain contexts.
No. JSX compiles to React.createElement() calls (or _jsx() with the new transform) at build time, so the runtime output is identical. Use whichever syntax you find more readable — for most developers, that's JSX.
Start by identifying logical sections of the page (header, main content, sidebar, footer) and creating a component for each. Convert the HTML within each section to JSX, extract dynamic content into props or state, and replace static links with React Router navigation. Use an HTML-to-JSX converter for the mechanical syntax changes, then refactor for React patterns.
Try our HTML to JSX Converter — paste your HTML and get clean, production-ready JSX in seconds.