DeveloperApril 2026 · 14 min read

How to Convert HTML to JSX: The Complete Developer's Guide

Convert HTML markup to valid React JSX — the attribute renames, the inline-style object transform, the self-closing void elements, and the few cases where the converter hands the wiring back to you.

Try the HTML to JSX Converter
Free, no signup
DG
Derek Giordano
Designer & Developer
In this guide
01Why HTML Isn't JSX02Attribute Renames (The Big List)03Event Handlers: camelCase and Functions04Self-Closing Void Elements05Inline Styles: String to Object06HTML Comments to JSX Comments07Boolean Attributes08Wrapping the Output as a Component09Edge Cases and Gotchas10Common Workflows11HTML vs JSX: Quick Reference12What to Do After Converting13Frequently Asked Questions
⚡ Key Takeaways
  • Convert HTML to React JSX.
  • Why HTML Isn't JSX.
  • Covers attribute renames (the big list).
  • Covers event handlers: camelcase and functions.
  • Covers self-closing void elements.

Why HTML Isn't JSX

If you've ever pasted a snippet of HTML into a React component expecting it to just work, you've seen the problem. The browser console lights up with warnings — Invalid DOM property 'class', Unknown event handler property 'onclick', Inline style should be a JavaScript object — and half your markup silently stops behaving the way it did on a plain HTML page.

JSX *looks* like HTML, which is the whole point. React's designers wanted markup to feel familiar. But JSX is JavaScript, not HTML, and JavaScript comes with its own rules: class is a reserved keyword, event handlers are functions not strings, and CSS inline styles are objects because you need to compose them at runtime.

The HTML to JSX Converter runs through the mechanical fixups in one paste: attribute renames (classclassName, forhtmlFor, 30+ others), event handlers to camelCase, void elements self-closed, inline styles parsed into JavaScript objects, and HTML comments converted to the {/* */} JSX form. Optionally it wraps the whole thing in a default-exported functional component with a name you choose.

This guide walks through what the converter handles, the specific rename rules, the style-object conversion, and the few edge cases — SVG namespaces, boolean attributes, inline scripts — where you'll still want to look at the output before committing.

Attribute Renames (The Big List)

The single largest category of HTML-to-JSX changes is attributes that got renamed because their HTML names conflict with JavaScript reserved words or don't match JavaScript's camelCase convention. Here's the full list the converter knows about:

💡 Tip
Use 3+ color stops instead of 2 to avoid the muddy gray band that appears in the center of complementary-color gradients.

The two most common — you'll see these on every paste:

class → className for → htmlFor

class is a reserved word in JavaScript (introduced with ES6's class syntax). for is the for loop keyword. React uses className and htmlFor to sidestep both.

Attributes that became camelCase:

tabindex → tabIndex readonly → readOnly maxlength → maxLength minlength → minLength cellspacing → cellSpacing cellpadding → cellPadding rowspan → rowSpan colspan → colSpan usemap → useMap frameborder → frameBorder contenteditable → contentEditable crossorigin → crossOrigin datetime → dateTime enctype → encType formaction → formAction formenctype → formEncType formmethod → formMethod formnovalidate → formNoValidate formtarget → formTarget hreflang → hrefLang inputmode → inputMode novalidate → noValidate radiogroup → radioGroup spellcheck → spellCheck srcdoc → srcDoc srclang → srcLang srcset → srcSet autocomplete → autoComplete autofocus → autoFocus autoplay → autoPlay playsinline → playsInline

Hyphenated attributes that become camelCase:

accept-charset → acceptCharset http-equiv → httpEquiv
**Note on data-* and aria-*.** These stay hyphenated in JSX — React treats them specially. data-testid, aria-label, aria-describedby all work as-is without renames. The converter leaves them alone.

The conversion is case-insensitive, so ONCLICK, OnClick, and onclick all become onClick. The pattern uses word boundaries, so a string like class="noclass" inside a value (not an attribute name) won't get touched.

Event Handlers: camelCase and Functions

HTML event handlers are strings. JSX event handlers are functions. The converter handles the naming part; you have to handle the value part.

⚠ Warning
CSS gradients used as backgrounds cannot be animated with standard transitions. Use background-size animation or @property registered custom properties instead.

What the converter does. Every on= attribute gets capitalized:

onclick → onClick onmouseover → onMouseOver onkeydown → onKeyDown onchange → onChange onsubmit → onSubmit onfocus → onFocus onblur → onBlur ontouchstart → onTouchStart

The pattern is simple: on, then the event name with its first letter capitalized. The regex handles any on* attribute, so less common events (onpointerdown, onwheel, ontransitionend) all get the same treatment.

What the converter does NOT do. The value stays a string. So this:

Becomes this:

Which *looks* right but isn't — React expects a function reference, not a string. You'll get a console warning (Expected onClick listener to be a function, instead got a value of 'string' type), and the handler won't fire. To fix it:

Or inline:

This is deliberate — the converter can't invent a function that doesn't exist, and silently generating something like onClick={() => eval("submit()")} would be worse than leaving the wiring for you.

Self-Closing Void Elements

Void elements are HTML tags that can't have children — , ,
,


, , , , , , , , , , . In HTML you can write without a closing tag and the parser knows what to do. In JSX, you can't — all tags must either have a matching close tag or be self-closed with />.

The converter handles this automatically. Every void element gets the /> suffix added:

Logo // becomes Logo // becomes
// becomes

The regex is clever about not double-closing things that are already self-closed — so stays as , it doesn't become . The lookbehind (? in the pattern skips anything already ending in /.

Why JSX requires this. JSX compiles to React.createElement(tag, props, ...children) calls. Without a close tag or self-close marker, the parser has no way to know where the element ends. next could mean "img followed by span" or "img with span as a child." Self-closing removes the ambiguity.

Inline Styles: String to Object

This is the most transformative conversion the tool does, and the one that saves the most typing. In HTML:

Hi

In JSX:

Hi

The converter splits the style string on semicolons, parses each property: value pair, camelCases the property name (kebab-case → camelCase), and wraps everything in a double-braced JSX expression — the outer {} is the JSX expression delimiter, the inner {} is the JS object literal.

Rules the converter follows:

A subtle point: values stay strings. React accepts both strings and numbers for most properties, but the converter always emits strings. For properties that take unitless numbers (like zIndex, opacity, flexGrow), you can manually remove the quotes after converting — but it's not required for correctness.

What doesn't convert. The converter only handles inline styles. Stylesheets in