HTML to JSX Converter
Convert HTML to JSX for React. Renames class→className, for→htmlFor, parses style strings to objects, self-closes void elements, handles SVG attrs.
| HTML | JSX | Note |
|---|---|---|
class="x" | className="x" | JSX uses className because class is a reserved word in JavaScript |
for="x" | htmlFor="x" | JSX uses htmlFor because for is reserved in JavaScript |
tabindex="0" | tabIndex="0" | All HTML attributes camelCase in JSX |
onclick="f()" | onClick={f} | Event handlers expect a function reference, not a string of code |
style="color:red" | style={{color: 'red'}} | JSX style expects an object with camelCase keys, values as strings or numbers |
<br> | <br /> | All elements must be properly closed in JSX (void elements use self-close) |
<!-- c --> | {/* c */} | JavaScript expressions in JSX live inside {curly braces} |
stroke-width="2" | strokeWidth="2" | SVG presentation attributes use camelCase in JSX (but viewBox, xmlns stay as-is) |
HTML to JSX Converter — React-Ready JSX from Plain HTML
Paste any HTML snippet and get JSX that compiles cleanly in a React component. The converter handles every common difference: renames class to className and for to htmlFor, parses style strings into JavaScript objects with camelCase keys, self-closes void elements, converts SVG hyphenated attributes, rewrites HTML comments as JSX expressions, and wraps event handlers in arrow functions. Optionally wraps the output in a Fragment or a full function component, with adjustable indent and a component-name input.
Why can't React just accept HTML directly?
JSX is a syntax extension to JavaScript, not HTML — and JavaScript has its own reserved words that conflict with HTML attribute names. The two big ones are:
- **class** — already used in JavaScript for class declarations (`class MyComponent extends React.Component`).
- **for** — used in for-loops (`for (let i = 0; ...)`).
React renamed those attributes to **className** and **htmlFor** to dodge the conflict. Every other camelCase rename (tabIndex, readOnly, autoComplete…) follows the same pattern: JSX uses camelCase for multi-word DOM properties since JSX expressions are evaluated as JavaScript object literals, where dashes aren't allowed in property names.
React 19 actually started accepting many HTML-spelled attributes (`tabindex`, `readonly`) at runtime, but the linter and most code conventions still want the camelCase form. This converter outputs strict camelCase for compatibility with React 16 through 19.
How does style get converted from a string to an object?
HTML style strings are key-value pairs separated by semicolons:
```
style="margin-top: 16px; background: #eee; font-weight: 700;"
```
JSX needs a JavaScript object whose keys are camelCase and whose values are either strings or numbers:
```
style={{ marginTop: '16px', background: '#eee', fontWeight: 700 }}
```
The converter splits on semicolons, splits each pair on its first colon, converts the property name to camelCase (`margin-top` → `marginTop`), and decides whether to quote the value. Pure numeric values (`16`, `700`, `1.5`) are emitted unquoted — React treats numeric values for size properties as px implicitly. Everything else is quoted as a string.
A few subtle behaviors:
- CSS custom properties (`--my-var`) keep their hyphens — they're quoted as string keys: `'--my-var': 'value'`.
- `!important` is stripped (JSX style doesn't support it natively; use a real CSS class instead).
- Vendor prefixes get camelCased too: `-webkit-transform` → `WebkitTransform` (note the capital W, per React convention).
If you'd rather paste the style as a raw string and not convert, uncheck "Convert style attr to object" — JSX accepts it as a string, though most React linters complain.
How does the converter handle event handlers like onclick?
In HTML, event handler attributes hold a string of JavaScript that the browser will eval when the event fires:
```
<button onclick="track('signup'); openModal()">Sign up</button>
```
JSX event handlers need a function reference (or an inline arrow function), not a string. The converter wraps the original code in an arrow function so it still runs:
```
<button onClick={() => { track('signup'); openModal() }}>Sign up</button>
```
This is intentionally a literal wrap — it preserves the existing logic so the snippet compiles, but you'll usually want to refactor that into a named handler:
```
<button onClick={handleSignup}>Sign up</button>
```
The "Strip event handlers" option removes inline handlers entirely if you'd rather attach them yourself in the JSX file. Useful when migrating legacy markup that has inline handlers everywhere.
The attribute renaming itself is mechanical: `onclick` → `onClick`, `onmouseover` → `onMouseOver`, `oninput` → `onInput`, etc. The converter recognizes any `on[a-z]+` attribute as an event handler.
Why do I see {'{'} and < in the output sometimes?
JSX is parsed as JavaScript, so the characters `{`, `}`, `<`, and `>` have syntactic meaning inside markup:
- `{` opens a JavaScript expression
- `<` opens a JSX tag
If those characters appear in text content, JSX treats them as code, not text. The converter escapes them so React renders them as literal characters:
- A literal `{` becomes `{'{'}`
- A literal `}` becomes `{'}'}`
- `<` becomes `<` (HTML entity, which JSX preserves in text content)
- `>` becomes `>`
This matters most for code-display blocks. If you paste `<code>const x = { a: 1 };</code>`, the JSX output will read `<code>const x = {'{'} a: 1 {'}'};</code>` — ugly but functionally correct.
For production, use a dedicated syntax-highlighting component (Prism, Shiki, Highlight.js) or wrap the literal code in `<pre>{`code here`}</pre>` with a template literal — JSX expressions can hold any string. The converter's escaping is the safe fallback when you don't want to refactor.
What does the converter do with SVG attributes?
SVG has dozens of presentation attributes that use hyphens: `stroke-width`, `fill-opacity`, `text-anchor`, `font-family`, and so on. In JSX these all need to be camelCase: `strokeWidth`, `fillOpacity`, `textAnchor`, `fontFamily`.
The converter recognizes the standard list of SVG hyphenated attributes (about 35 of them, per the React DOM API) and only converts those — `data-*` and `aria-*` attributes always keep their hyphens because JSX allows hyphens specifically in those two namespaces.
A few SVG attributes are *already* camelCase in HTML and stay unchanged: `viewBox`, `preserveAspectRatio`, `gradientUnits`, `xmlns`. The HTML5 spec defined these as case-sensitive CamelCase, and JSX inherits that.
A full SVG snippet from a typical icon library — paths, transforms, gradients — converts cleanly with one paste. The transformation log shows you which attributes were touched so you can spot anything unusual.
Should I wrap the output in a Fragment or a component?
Pick based on where the markup is going:
- **No wrapper** — the converter outputs raw JSX you can paste anywhere. Best when integrating into an existing component's return statement.
- **Fragment (`<></>`)** — wraps multiple top-level elements in a React Fragment. Use when your HTML has more than one root (e.g., two sibling divs); JSX requires a single root, and Fragments don't add a wrapper element to the DOM.
- **Function component** — wraps everything in a complete `function MyComponent() { return (...); }` definition. Best for quick scaffolding when starting a new component file from a design mockup.
The component-name field is honored only in component mode. It's restricted to alphanumeric characters and must start with a capital (React convention — components must start with an uppercase letter so JSX distinguishes them from HTML tags).
None of the wrapper modes inject `import React from 'react'` — modern React (17+) with the JSX transform doesn't need it. If you're on older React, add the import manually.
What about React DOM attributes I'd expect but aren't here?
The converter handles the common case. A few attributes have nuances worth knowing:
- **value vs defaultValue** — In HTML, `<input value="x">` sets initial value. In JSX, that creates a *controlled* input that must have an onChange. If you want HTML's behavior (initial value, then user can edit), use `defaultValue="x"`. The converter leaves `value` alone — you'll need to decide controlled vs uncontrolled per case.
- **checked vs defaultChecked** — Same controlled/uncontrolled distinction for checkboxes/radios.
- **selected on `<option>`** — Use `value` on the parent `<select>` instead, that's React idiom.
- **dangerouslySetInnerHTML** — React's only way to set HTML directly. The converter doesn't generate this; it converts text content to JSX text nodes instead.
- **ref** — A React-specific prop the converter doesn't generate; add manually when wiring focus or DOM measurements.
The converter handles the *syntactic* migration — getting your HTML to compile as JSX. The *semantic* migration (controlled inputs, refs, state) is something you'll layer on top depending on what the component needs to do.
Is this converter private?
Yes. The conversion happens entirely in your browser:
- The page uses the browser's built-in DOMParser to parse the HTML — no remote API is called.
- The transformation rules are JavaScript code that runs locally.
- The Sample button populates the textarea with a built-in string; nothing is downloaded.
- The Copy button uses the browser's clipboard API.
- No telemetry of what HTML you pasted.
Verify by opening DevTools → Network and watching the panel while you click Convert — no requests fire. This also means the tool works offline once the page has loaded, so you can convert markup snippets in a hotel, on a plane, or behind a corporate firewall without worrying about where your code is going.
Key Features
- Browser-native DOMParser for robust HTML parsing (handles broken/unclosed markup gracefully)
- class → className and for → htmlFor renaming
- tabindex → tabIndex, readonly → readOnly, and 25+ other camelCase remappings
- Event handlers (onclick, onchange, etc.) renamed to camelCase and wrapped in arrow functions
- Inline style strings parsed into JavaScript objects with camelCase keys
- SVG hyphenated attributes (stroke-width, fill-opacity, text-anchor) converted to camelCase
- Void elements (br, img, input) auto self-closed for JSX strict mode
- HTML comments converted to {/* ... */} JSX expressions
- Boolean attributes (disabled, checked, required) emitted without value
- data-* and aria-* attributes preserved as-is per JSX spec
- Three wrapping modes: none, Fragment, or full function component
- Configurable indent: 2 spaces, 4 spaces, or tabs
- Component name input for function-component mode
- Strip event handlers option for clean migration
- Transformation log shows exactly what was changed
- Pure JavaScript — no external libraries
- Works offline after first load
- 100% client-side — your HTML stays in your browser
