HTML to JSX: The Complete Conversion Guide for React (2026)

Published on March 27, 2026 by Suvom Das

Whether you are migrating an existing website to React, converting an HTML email template into a component, or simply pasting a snippet from Stack Overflow, you will inevitably need to convert HTML to JSX. While JSX looks deceptively similar to HTML, there are several critical differences that cause compilation errors if not handled correctly. This comprehensive guide covers every transformation you need to know, from the basics like class to className all the way to edge cases involving comments, fragments, and embedded SVG.

What Is JSX and Why Is It Different from HTML?

JSX stands for JavaScript XML. It is a syntax extension for JavaScript that lets you write markup inside JavaScript code. When you write <div className="container">Hello</div> in a React component, the JSX compiler (Babel or TypeScript) transforms it into a React.createElement('div', { className: 'container' }, 'Hello') function call. This is why JSX has to follow JavaScript rules rather than HTML rules.

The key insight is that JSX attributes become JavaScript object properties. Since JavaScript object properties cannot contain hyphens (they would be interpreted as subtraction) and cannot use reserved keywords as names, several HTML attributes need alternative names in JSX. Understanding this fundamental principle makes all the conversion rules intuitive.

The Complete HTML to JSX Conversion Reference

1. class Becomes className

This is the most common conversion. In JavaScript, class is a reserved keyword used for defining classes (class MyComponent extends React.Component). Since JSX compiles to JavaScript, using class as a property name would create a syntax error. React uses className instead.

<!-- HTML -->
<div class="container">
  <p class="text-bold text-primary">Hello</p>
</div>

{/* JSX */}
<div className="container">
  <p className="text-bold text-primary">Hello</p>
</div>

Note that if you are using a CSS-in-JS library like styled-components or Emotion, you may not use className at all. But when working with regular CSS files, CSS modules, or utility frameworks like Tailwind CSS, className is essential.

2. for Becomes htmlFor

The HTML for attribute on <label> elements associates the label with a form control. In JavaScript, for is a reserved keyword for loops (for (let i = 0; ...)). React uses htmlFor instead.

<!-- HTML -->
<label for="email">Email:</label>
<input type="email" id="email" />

{/* JSX */}
<label htmlFor="email">Email:</label>
<input type="email" id="email" />

3. Inline Style Strings Become JavaScript Objects

This is one of the trickiest conversions because it involves three changes at once: the attribute value format changes from a string to an object, CSS property names change from kebab-case to camelCase, and the syntax uses double curly braces.

<!-- HTML -->
<div style="background-color: #f0f0f0; font-size: 14px; margin-top: 20px; border-radius: 8px;">
  Content
</div>

{/* JSX */}
<div style={{ backgroundColor: '#f0f0f0', fontSize: '14px', marginTop: '20px', borderRadius: '8px' }}>
  Content
</div>

Here is a reference table for the most common CSS property conversions:

CSS PropertyJSX Style Property
background-colorbackgroundColor
font-sizefontSize
font-weightfontWeight
font-familyfontFamily
text-aligntextAlign
text-decorationtextDecoration
text-transformtextTransform
line-heightlineHeight
letter-spacingletterSpacing
margin-topmarginTop
margin-bottommarginBottom
padding-leftpaddingLeft
padding-rightpaddingRight
border-radiusborderRadius
border-bottomborderBottom
box-shadowboxShadow
z-indexzIndex
max-widthmaxWidth
min-heightminHeight
flex-directionflexDirection
justify-contentjustifyContent
align-itemsalignItems

For numeric values without units, you can use plain numbers instead of strings. React will automatically append px for properties that accept pixel values:

{/* These are equivalent */}
<div style={{ fontSize: '14px', marginTop: '20px' }}>
<div style={{ fontSize: 14, marginTop: 20 }}>

4. Self-Closing Tags

In HTML, void elements like <img>, <br>, <input>, and <hr> do not need a closing slash. In JSX, all elements must be properly closed. Void elements must use self-closing syntax.

<!-- HTML -->
<img src="photo.jpg" alt="Photo">
<br>
<input type="text">
<hr>

{/* JSX */}
<img src="photo.jpg" alt="Photo" />
<br />
<input type="text" />
<hr />

The complete list of HTML void elements that need self-closing in JSX: area, base, br, col, embed, hr, img, input, link, meta, param, source, track, wbr.

5. Boolean Attributes

HTML boolean attributes like disabled, checked, required, and readonly can be written as standalone attributes in both HTML and JSX. However, some attribute names change.

<!-- HTML -->
<input type="text" readonly disabled>
<select multiple>...</select>
<input type="checkbox" checked>

{/* JSX */}
<input type="text" readOnly disabled />
<select multiple>...</select>
<input type="checkbox" defaultChecked />

Note that readonly becomes readOnly (camelCase), and for uncontrolled form elements, checked should become defaultChecked and value should become defaultValue to avoid React controlled component warnings.

6. Event Handlers

HTML event handler attributes use lowercase: onclick, onchange, onsubmit, onmouseover. JSX uses camelCase: onClick, onChange, onSubmit, onMouseOver. Additionally, JSX event handlers receive a SyntheticEvent object rather than the native event, and the handler value should be a JavaScript function reference rather than a string of code.

<!-- HTML -->
<button onclick="handleClick()">Click</button>
<input onchange="handleChange(event)">

{/* JSX */}
<button onClick={handleClick}>Click</button>
<input onChange={handleChange} />

Common event handler mappings:

HTML EventJSX Event
onclickonClick
onchangeonChange
onsubmitonSubmit
onfocusonFocus
onbluronBlur
onkeydownonKeyDown
onkeyuponKeyUp
onmouseoveronMouseOver
onmouseoutonMouseOut
oninputonInput

7. HTML Comments Become JSX Comments

HTML comments (<!-- comment -->) are not valid inside JSX. They must be converted to JavaScript comments wrapped in curly braces.

<!-- HTML -->
<div>
  <!-- This is a navigation section -->
  <nav>...</nav>
</div>

{/* JSX */}
<div>
  {/* This is a navigation section */}
  <nav>...</nav>
</div>

8. Multiple Root Elements Need Fragments

In HTML, you can have multiple sibling elements without a wrapper. JSX requires a single root element. If you have multiple top-level elements, wrap them in a React Fragment.

<!-- HTML -->
<h1>Title</h1>
<p>Description</p>
<button>Action</button>

{/* JSX - using Fragment */}
<>
  <h1>Title</h1>
  <p>Description</p>
  <button>Action</button>
</>

The short syntax <>...</> is equivalent to <React.Fragment>...</React.Fragment>. Use the long form when you need to add a key prop (common in lists).

9. Other Attribute Renames

Beyond class and for, several other HTML attributes have different names in JSX:

HTML AttributeJSX Attribute
tabindextabIndex
readonlyreadOnly
maxlengthmaxLength
cellpaddingcellPadding
cellspacingcellSpacing
rowspanrowSpan
colspancolSpan
enctypeencType
contenteditablecontentEditable
crossorigincrossOrigin
accesskeyaccessKey
autocompleteautoComplete
autofocusautoFocus
autoplayautoPlay

10. data-* and aria-* Stay Unchanged

Good news: data-* and aria-* attributes are the exception. They remain exactly as written in HTML. You can use data-testid="my-element" and aria-label="Close" in JSX without any changes. These are the only hyphenated attributes that React allows in their original form.

Real-World Conversion Examples

Converting a Navigation Bar

<!-- HTML -->
<nav class="navbar">
  <a class="logo" href="/">
    <img src="/logo.png" alt="Logo">
  </a>
  <ul class="nav-links">
    <li><a href="/about" class="active">About</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
  <button class="menu-btn" onclick="toggleMenu()">
    <!-- menu icon -->
  </button>
</nav>

{/* JSX */}
<nav className="navbar">
  <a className="logo" href="/">
    <img src="/logo.png" alt="Logo" />
  </a>
  <ul className="nav-links">
    <li><a href="/about" className="active">About</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
  <button className="menu-btn" onClick={toggleMenu}>
    {/* menu icon */}
  </button>
</nav>

Converting a Form

<!-- HTML -->
<form class="signup-form" onsubmit="handleSubmit(event)">
  <label for="username">Username:</label>
  <input type="text" id="username" class="form-input"
         maxlength="50" autofocus tabindex="1"
         style="border: 1px solid #ccc; padding: 8px;">

  <label for="email">Email:</label>
  <input type="email" id="email" class="form-input"
         required autocomplete="email" tabindex="2">

  <label for="terms">
    <input type="checkbox" id="terms" checked>
    I agree to the terms
  </label>

  <button type="submit" disabled class="btn-primary">Submit</button>
</form>

{/* JSX */}
<form className="signup-form" onSubmit={handleSubmit}>
  <label htmlFor="username">Username:</label>
  <input type="text" id="username" className="form-input"
         maxLength="50" autoFocus tabIndex="1"
         style={{ border: '1px solid #ccc', padding: '8px' }} />

  <label htmlFor="email">Email:</label>
  <input type="email" id="email" className="form-input"
         required autoComplete="email" tabIndex="2" />

  <label htmlFor="terms">
    <input type="checkbox" id="terms" defaultChecked />
    I agree to the terms
  </label>

  <button type="submit" disabled className="btn-primary">Submit</button>
</form>

Wrapping Converted JSX in a React Component

Once you have JSX-compatible markup, you typically want to wrap it in a React component. A functional component is the modern standard.

import React from 'react';

const SignupForm = () => {
  const handleSubmit = (e) => {
    e.preventDefault();
    // Handle form submission
  };

  return (
    <form className="signup-form" onSubmit={handleSubmit}>
      {/* ... converted JSX here ... */}
    </form>
  );
};

export default SignupForm;

For TypeScript, add type annotations:

import React from 'react';

const SignupForm: React.FC = () => {
  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
  };

  return (
    <form className="signup-form" onSubmit={handleSubmit}>
      {/* ... */}
    </form>
  );
};

export default SignupForm;

Common Mistakes to Avoid

Forgetting to Close Void Elements

The most frequent JSX error for beginners is forgetting the self-closing slash on void elements. <img src="..."> compiles fine in HTML but throws a syntax error in JSX. Always write <img src="..." />.

Using String Event Handlers

In HTML, onclick="alert('clicked')" executes a string of JavaScript. In JSX, event handlers must be function references: onClick={() => alert('clicked')}. Passing a string will not cause an error but it will not work either, as React will ignore it.

Leaving CSS String Styles

React will warn you at runtime if you pass a string to the style prop: "The style prop expects a mapping from style properties to values, not a string." Always convert to a JavaScript object.

Using class Instead of className

Modern React (17+) will actually still render the element correctly even if you use class instead of className, but it will print a warning in the console. It is best practice to always use className for clean, warning-free code.

Automating HTML to JSX Conversion

For quick, one-off conversions, our HTML to JSX converter tool handles all the transformations described in this guide instantly. Paste your HTML, and the tool converts class to className, for to htmlFor, style strings to objects, event handlers to camelCase, ensures self-closing tags, converts comments to JSX syntax, and wraps multiple roots in fragments. You can optionally generate a complete React component with TypeScript types.

For build-time automation in large projects, consider configuring your bundler to handle the conversion automatically or using ESLint rules like react/no-unknown-property to catch unconverted HTML attributes during development.

Try the HTML to JSX Converter

Frequently Asked Questions

Why does React use className instead of class?
In JavaScript, class is a reserved keyword used for defining classes with the class syntax (e.g., class MyComponent extends React.Component). Since JSX compiles to JavaScript, using class as an attribute name would create a syntax conflict. React uses className as the JSX equivalent to avoid this collision.
How do I handle inline styles when converting HTML to JSX?
In HTML, the style attribute is a CSS string: style="color: red; font-size: 14px". In JSX, it must be a JavaScript object with camelCased property names: style={{ color: 'red', fontSize: '14px' }}. Hyphenated CSS properties like font-size become fontSize, background-color becomes backgroundColor, and so on. Numeric pixel values can optionally be written as plain numbers.
What happens to HTML comments in JSX?
HTML comments like <!-- comment --> are not valid JSX syntax. They must be converted to JavaScript comments wrapped in curly braces: {/* comment */}. This is because JSX treats everything between tags as either text content or JavaScript expressions, and HTML comment syntax is neither.
How does JSX handle boolean attributes like disabled?
In HTML, boolean attributes can be written as just the attribute name (e.g., <input disabled>) or with a value (e.g., <input disabled="disabled">). In JSX, you write them as just the attribute name (<input disabled />) which is equivalent to disabled={true}. Some boolean attributes also get renamed: readonly becomes readOnly and autofocus becomes autoFocus.
Do data-* and aria-* attributes change in JSX?
No. React supports data-* and aria-* attributes exactly as they are in HTML. You can write data-testid="my-element" and aria-label="Close button" in JSX without any changes. These are the only hyphenated attributes that React does not require in camelCase, as they are defined by web standards and widely used for testing and accessibility.