/

HTML to JSX Converter

Processed Client Side

Paste HTML and get valid JSX — class becomes className, inline styles become objects, and every attribute is renamed the way React expects. Runs entirely in your browser.

HTML · 8 lines · 491 chars
HTMLLength: 491Lines: 8Size: 491 BytesCursor: 1:1
JSX
Converted
JSXLength: 752Lines: 28Size: 756 BytesCursor: 1:1
9 elements13 attributes1 commentnesting depth 4

Bookmark this tool now — skip the search next time you need it.

About HTML to JSX Converter

This tool runs entirely in your browser. Whatever you paste is processed on your own device and is never uploaded, logged, or sent to any server.

Pasting HTML straight into a React component does not work, and the reasons are scattered: `class` is a reserved word, `for` is a statement, `onclick` holds a string where JSX wants a function, and `style` is a CSS declaration list where React wants an object. This converter fixes all of them at once. It parses the markup into a tree first — rather than running find-and-replace over the text — which is what lets it know that the `<` in `if (a < b)` inside a script is not a tag, that the space between two inline elements is rendered and must survive being put on its own line, and that a `<li>` you never closed still ends where the browser ends it. Everything runs in your browser, so an unreleased template is never uploaded.

Key features

  • Every attribute renamed the way React expects: class to className, for to htmlFor, tabindex to tabIndex, and the whole camelCase set down to onCanPlayThrough
  • data-* and aria-* attributes pass through untouched, and so do custom ones like ng-model or hx-get — only the hyphenated SVG presentation attributes are camelCased, because those are the ones React maps onto DOM properties
  • Inline `style` becomes a real style object, with vendor prefixes capitalised correctly (WebkitTransform, but msTransform) and custom properties kept as quoted keys
  • Inline handlers become functions: onclick="save()" comes out as onClick={() => save()}, and a handler that leans on `this` or `event` is flagged rather than quietly converted
  • Entities are decoded, and the invisible ones are written as escapes — a &nbsp; becomes {'\u00A0'} instead of a character you cannot tell from a space in a diff
  • JSX syntax characters in your text are escaped, so a stray {, }, < or > in the copy does not turn into a parse error
  • The awkward elements are handled individually: <pre> and <style> keep their content verbatim in a template literal, <textarea> gets defaultValue instead of children, and comments become {/* … */}
  • Output as bare JSX, a function component, or an arrow component, with a name you choose and 2-space, 4-space or tab indentation
  • Rendered whitespace is preserved across line breaks with an explicit {' '}, which is the difference between "Hello world" and "Helloworld" once the markup is formatted
  • Warnings for what React will complain about later — an unclosed tag, a <tr> with no <tbody>, an <option selected>, a prop that had to be dropped
  • Converts as you type, entirely in your browser — nothing you paste is uploaded

How to use it

  1. Paste your HTML into the left pane, replacing the sample.
  2. Choose what you want out: JSX only if you are pasting into an existing component, or a function or arrow component if you want a whole file.
  3. Name the component if you picked one of the component modes — the name is capitalised for you, because JSX reads a lower-case tag as an HTML element.
  4. Pick your indentation, and decide whether form fields should use value or defaultValue.
  5. Read the badges under the output: they count what was converted and flag anything React will object to.
  6. Copy the JSX or download it as a .jsx file.

Tips & common mistakes

  • Leave "value → defaultValue" on unless you are about to wire the field up. React treats an input with a `value` and no `onChange` as read-only and warns in the console; `defaultValue` is what makes it an ordinary uncontrolled field that people can type in.
  • The {' '} expressions in the output are load-bearing, not clutter. JSX deletes any whitespace run that contains a newline at the start or end of a line, so a space between two inline elements has to be written explicitly once the line is broken.
  • A <script> body cannot become JSX children, so it is moved to dangerouslySetInnerHTML — and React will not execute it there. Move the code into your component instead; the converted markup is a placeholder, not a working script tag.
  • Everything the converter cannot express is reported rather than silently dropped. `!important` in an inline style is the common one: React sets styles through the element’s style property, which rejects a declaration carrying a priority, so the flag comes off and the warning tells you where.
  • Framework attributes survive if they are valid JSX names and are dropped if they are not. `v-if` and `ng-model` come through as written; `@click`, `:href` and `(ngSubmit)` cannot be JSX props at all, and get a warning instead of output that will not parse.
  • Converting an SVG icon works well and is a common reason to use this. The hyphenated presentation attributes become camelCase, and the elements whose spelling matters — linearGradient, clipPath, feGaussianBlur — get their capitals back, which HTML parsing had lower-cased.
  • The output is formatted, not just translated: attributes break one per line past 80 columns and short elements stay on one line. If you run Prettier on your project, expect it to agree with most of this and reflow the rest — the conversion is what matters, the layout is a starting point.
  • This converts markup, not behaviour. An inline handler becomes an arrow function calling the same code, but that code still has to exist in scope, and `this` inside it no longer means the element it was written on.

Related tools

Browse all 10 Code tools

Frequently asked questions

11

Paste the markup into the left pane and the JSX appears on the right immediately, updating as you type. Choose whether you want bare JSX to paste into an existing component or a whole function or arrow component, pick your indentation, then copy the result or download it as a .jsx file. Nothing is uploaded — the conversion runs in your browser.

Because several HTML attributes are not legal JSX. `class` and `for` are reserved words in JavaScript, so React renames them to className and htmlFor. Multi-word attributes like tabindex and maxlength are camelCased. `style` holds a string of CSS in HTML and an object in React. `onclick` holds a string of JavaScript that the browser compiles for you, where JSX expects an actual function. And a bare `>` or `{` in your text is a parse error. Each one is a small change, but there are enough of them in a real block of markup that doing it by hand is where the typos come from.

A `style` attribute becomes a style object: `style="color:red;font-size:12px"` comes out as `style={{ color: 'red', fontSize: '12px' }}`. Values stay strings, because React adds a px suffix to a bare number on some properties and not others. Vendor prefixes get the capitalisation React actually wants — `-webkit-transform` becomes WebkitTransform, but `-ms-transform` becomes msTransform, which is the one exception. Custom properties keep their exact name as a quoted key, so `--brand` stays `--brand`.

The string of JavaScript becomes an arrow function: `onclick="save()"` converts to `onClick={() => save()}`, and a handler with several statements becomes a block body. The code itself is copied through unchanged, because it is already JavaScript and rewriting it would be guesswork. Two things do not survive the move and are flagged for you: `this` no longer refers to the element the attribute was written on, and `event` is no longer an implicit global — you want the handler’s own parameter instead.

Yes, and it is one of the better reasons to use it. Hyphenated presentation attributes are camelCased to match the React props — stroke-width to strokeWidth, clip-path to clipPath — and the elements whose spelling is case-sensitive get their capitals back, so linearGradient, clipPath and feGaussianBlur come out correctly even though HTML parsing lower-cased them. `xlink:href` becomes xlinkHref.

No. React forwards those exactly as written, so `data-id` and `aria-label` come through untouched. Custom hyphenated attributes are left alone too — `ng-model`, `hx-get` and `v-if` are all valid JSX prop names already, and camelCasing them would break them. What cannot be converted is template syntax that is not an attribute at all: `@click`, `:href`, `[value]` and `(ngSubmit)` are reported and dropped rather than turned into props that would not parse.

Rendered spaces that would otherwise disappear. JSX throws away any whitespace run containing a newline at the start or end of a line, so the moment `<b>Hello</b> <b>world</b>` is formatted onto two lines, the space between the words is gone. Wherever the converter breaks a line across a space that HTML would render, it writes that space out explicitly. They look like noise and they are the reason the output renders the same as the input.

Because JSX has no entity syntax — a `&nbsp;` written in a JSX text node reaches the page as those six literal characters, so every entity has to be decoded. Decoding it to a raw non-breaking space would leave a character in your source that is indistinguishable from an ordinary space in an editor or a diff, which is a genuinely nasty bug to chase later. Writing it as an escape keeps it visible. Ordinary entities like `&copy;` and `&mdash;` just become © and —, since those are unambiguous on sight.

Yes — that is deliberate, since it re-converts on every keystroke and markup you are still typing is unfinished by definition. An unclosed tag, a stray closing tag, or a tag missing its `>` all still produce output, with a warning next to it saying what was wrong. Optional closing tags are filled in the way a browser fills them, so `<ul><li>a<li>b</ul>` nests correctly and is not reported, because that is valid HTML rather than a mistake.

The output is valid JSX — that is what the escaping of `<`, `>`, `{` and `}` in text is for, and why a document with several top-level elements is wrapped in a fragment. Whether it *runs* is a separate question the converter tells you about rather than guesses at: an inline handler still needs its function to exist in your scope, and a `<script>` body is moved to dangerouslySetInnerHTML where React will not execute it. Those cases are warned about instead of being quietly left to fail.

No. The parser and the printer are JavaScript running in your browser tab, and there is no request that sends markup anywhere. That is what makes it safe to paste a template from an unreleased design, a client project, or an internal application.