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.
- 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 (class → className, for → htmlFor, 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:
The two most common — you'll see these on every paste:
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:
Hyphenated attributes that become camelCase:
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.
background-size animation or @property registered custom properties instead.What the converter does. Every on attribute gets capitalized:
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:
// becomes
// 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 /.
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:
In JSX:
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:
- Property names get camelCased:
background-color→backgroundColor,border-top-width→borderTopWidth,-webkit-transform→WebkitTransform - Values stay as strings (always quoted):
"red","16px","0 auto" - CSS custom properties (variables) starting with
--are preserved as-is and wrapped in quoted keys:{"--brand-color": "#FF6B6B"} - Invalid property names (ones that need quoting to be valid JS identifiers) get quoted automatically
- Empty values and malformed pairs are silently dropped
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 blocks or external CSS files stay as they are — which is usually what you want, since those don't have the JSX-object issue in the first place.
HTML Comments to JSX Comments
HTML comments look like . In JSX,
// becomes
{/* Form section */}