HTML to JSX Converter
Paste HTML and get React-ready JSX — React attribute names, camelCased inline style objects, self-closed void tags, JSX comments and escaped text, converted locally in your browser.
About this tool
HTML to JSX Converter turns plain HTML into React-ready JSX. It is the step you hit every time you paste markup from a design export, a CMS, a Bootstrap/Tailwind snippet, an email template, or an old server-rendered template into a React component and the compiler starts complaining about class, unclosed <img> tags, and string style attributes.
The converter rewrites everything JSX is strict about:
- React attribute names —
class→className,for→htmlFor,tabindex→tabIndex,readonly→readOnly,maxlength→maxLength,cellpadding→cellPadding,http-equiv→httpEquiv, plus the SVG set (stroke-width→strokeWidth,viewbox→viewBox,xlink:href→xlinkHref).data-*andaria-*stay hyphenated, because React wants them that way. - Inline styles — a
style="color: red; font-size: 12px"string becomes astyle={{ color: "red", fontSize: "12px" }}object with camelCased properties and string values. Vendor prefixes follow React's rules (-webkit-box-shadow→WebkitBoxShadow,-ms-flex→msFlex) and CSS custom properties keep their name as a quoted key ("--brand"). - Boolean attributes —
disabled,readonly,requiredand friends becomedisabled={true}, or stay bare if you prefer the shorthand. - Void tags —
<br>,<img>,<input>,<hr>and the rest are self-closed as<br />. - Comments —
<!-- note -->becomes{/* note */}, or is removed. - Safe text — literal
{and}in text are escaped as{'{'}/{'}'}so they aren't parsed as expressions. - Inline handlers —
onclick="save()"becomesonClick={() => { save() }}so React gets a function, not a string. - Multiple roots — several top-level nodes are wrapped in a
<>…</>fragment, which JSX requires.
Worked example
Input HTML:
<div class="card"><label for="n">Name</label><input id="n" tabindex="2" readonly></div>
JSX output:
<div className="card">
<label htmlFor="n">Name</label>
<input id="n" tabIndex={2} readOnly={true} />
</div>
With Wrap in a component set to Card and 4-space indentation, <p style="color: red; font-size: 12px">Hi</p> becomes:
export default function Card() {
return (
<p style={{ color: "red", fontSize: "12px" }}>Hi</p>
);
}
Options
- Indentation — 2 spaces (default), 4 spaces, or tabs.
- Wrap in a component — leave empty for a bare JSX snippet, or give a name to get
export default function Name() { return (…); }. - HTML comments — keep them as
{/* … */}expression containers, or drop them. - Boolean attributes —
disabled={true}(explicit, the default) or baredisabled. - value / checked on form fields — rewrite to
defaultValue/defaultCheckedoninput,textareaandselect(default) so React doesn't warn about an uncontrolled field that never updates, or keep the original names when you're wiring up controlled inputs yourself.
Limits and edge cases
- The parser is a forgiving HTML tokenizer, not a full HTML5 tree builder. It handles unclosed tags, implicit
</li>/</p>/</td>closes, unquoted attribute values and stray text, but it does not reconstruct a document the way a browser would (no table-scoping repair, no foster parenting). - Whitespace between elements is collapsed the way a browser renders it, so the output is indented and readable.
<pre>and<textarea>content is preserved verbatim. <script>and<style>contents are emitted verbatim inside a JSX template-literal child, so the CSS or JS is preserved exactly. That compiles, but for real projects a stylesheet import ordangerouslySetInnerHTMLis usually the better home.- A
<!DOCTYPE html>declaration has no JSX equivalent and is dropped. - Inline
on*handlers are wrapped in an arrow function verbatim — the JavaScript inside is not analysed, so the referenced functions still have to exist in your component's scope. - HTML entities such as
and&are left as-is: JSX decodes them in text and in attribute strings.
FAQ
Why does JSX need `className` instead of `class`?
JSX compiles to JavaScript, and class is a reserved word there, so React named the DOM property className (the same name the browser's DOM API uses). for has the same problem — it becomes htmlFor. This tool renames both, along with the rest of React's camelCase attribute set.
Why did my `style="…"` string turn into double braces?
React expects a JavaScript object for style, not a CSS string. The outer braces are the JSX expression container and the inner ones are the object literal, which is why it reads as style={{ … }}. Property names are camelCased (font-size → fontSize) and values stay strings, so units like px and % survive untouched.
What happens to `value` and `checked` on a form field?
By default they become defaultValue and defaultChecked. React treats value / checked as a controlled value: without an onChange handler the field would be frozen and React would log a warning. defaultValue / defaultChecked gives you the same initial state with an editable field. Switch the option to Keep as value / checked if you're adding state and a change handler yourself.
Can I convert a whole HTML page?
Yes — paste it and you'll get the <html>/<head>/<body> tree as JSX, with the doctype dropped. In practice you usually want one component's worth of markup: React apps rarely render <html> directly outside a framework's document file, and a full page pulls in <meta>/<script> tags that belong in your framework's head configuration.
Is my HTML uploaded anywhere?
No. The page runs a WebAssembly converter in your browser. The markup is parsed and converted locally, and you can copy or download the JSX without the content leaving your device.
Does it handle SVG icons?
Yes. SVG attributes are camelCased the way React expects (stroke-width → strokeWidth, stroke-linecap → strokeLinecap, viewbox → viewBox, xlink:href → xlinkHref), and camelCase SVG element names such as <linearGradient> and <clipPath> are preserved even if the source wrote them in lowercase.
Developer & Automation Access
Run it from the terminal
Same engine as this page, headless — via the gizza CLI:
gizza tool html-to-jsx '<div class="card"><label for="n">Name</label><input id="n" tabindex="2" readonly></div>'New to the CLI? Get gizza →
Open it by URL
Pre-fill and auto-run this tool with query parameters — the names match the API/CLI:
https://gizza.ai/tools/html-to-jsx/?html=%3Cdiv%20class%3D%22card%22%3E%3Clabel%20for%3D%22n%22%3EName%3C%2Flabel%3E%3Cinput%20id%3D%22n%22%20tabindex%3D%222%22%20readonly%3E%3C%2Fdiv%3E&indent=2&component=Card&comments=jsx&boolean_attrs=explicit&value_attrs=defaultMachine-readable descriptor: tool.json — title + parameters JSON Schema for agents.
