About the Transforms
Some frontend chores are pure mechanical translation. Pasting HTML into JSX means renaming class to className, closing every void element, and rewriting inline styles as objects. Turning a designer's SVG into a component means the same attribute renaming plus wrapping it in a function. Neither is difficult and both are tedious enough to get wrong.
These transforms do the mechanical part: HTML to JSX, SVG to a React component, CSS to a JS style object, and Markdown to HTML.
When you'd use it
- Pasting a snippet from a template or a documentation site into a React component.
- Turning an exported SVG icon into a component with prop spreading, instead of an img tag.
- Converting a CSS block into the object form a CSS-in-JS library or an inline style prop expects.
- Rendering Markdown to HTML for a CMS field or an email template.
HTML to JSX
class becomes className, for becomes htmlFor, void elements self-close, and style strings become objects.
HTML
<label for="email" class="field" style="margin-top: 8px">
Email
<input type="email" id="email">
</label>JSX
<label htmlFor="email" className="field" style={{ marginTop: '8px' }}>
Email
<input type="email" id="email" />
</label>CSS to a JS style object
Hyphenated properties become camelCase; values stay as strings so units survive.
CSS
background-color: #1e293b;
border-radius: 8px;
-webkit-font-smoothing: antialiased;JS object
{
backgroundColor: '#1e293b',
borderRadius: '8px',
WebkitFontSmoothing: 'antialiased',
}What HTML to JSX actually changes
JSX looks like HTML but is compiled to function calls, and a handful of names collide with JavaScript reserved words or with DOM property names. The transform handles all of them.
- class becomes className and for becomes htmlFor, because class and for are reserved words.
- Void elements — img, input, br, hr, meta, link — must be explicitly self-closed. HTML allows leaving them open; JSX does not.
- Hyphenated attributes become camelCase: tabindex to tabIndex, colspan to colSpan, maxlength to maxLength. The exceptions are data-* and aria-*, which keep their hyphens.
- Inline style strings become objects with camelCase keys, since JSX style takes an object rather than a string.
- HTML comments become JSX expression comments, because the HTML comment syntax is not valid inside JSX.
SVG to a React component
SVG has more hyphenated attributes than HTML — stroke-width, fill-rule, clip-path, stroke-linecap — and every one of them has to be camelCased for JSX. Inline <style> blocks inside an SVG need their braces escaped. Getting one wrong produces an icon that renders subtly incorrectly rather than failing loudly.
The output is a complete functional component with a props spread, not just an attribute-converted string. That spread is what lets you size and colour the icon at the call site — passing width, className, or aria-hidden without editing the component.
One thing to check by hand: an SVG with a hard-coded fill will ignore CSS colour. Replacing the fill value with currentColor makes the icon inherit text colour, which is usually what you want.
ID collisions when you inline SVGs
SVG gradients, filters, masks, and clip paths are referenced by id, and ids are global to the document. Two icons exported from the same design tool commonly both contain an element with id="a" or id="gradient-1". Rendered on the same page, the second definition wins and one icon renders with the other's gradient.
This is not something the transform can fix, because it only sees one file. If an inlined icon renders with the wrong colours once other icons are on the page, prefix its ids to make them unique.
Markdown to HTML
The Markdown transform implements CommonMark plus GitHub extensions — tables, strikethrough, task lists, and fenced code blocks. Output is sanitised, so script tags and event handler attributes embedded in the source are stripped rather than passed through.
That sanitisation matters if the Markdown came from anywhere you do not control. Markdown permits raw HTML by design, which makes untrusted Markdown a cross-site scripting vector unless the output is cleaned.
Frequently asked questions
- Does HTML to JSX handle a whole page?
- It converts whatever you give it, but a whole page pasted into one component is rarely what you want. Convert it, then split the result into components.
- Why did my style attribute values keep their quotes?
- JSX style values are JavaScript, and a value with units is a string. Only unitless numeric properties — zIndex, opacity, flexGrow, lineHeight — can be bare numbers.
- Can I get a TypeScript component from SVG to JSX?
- The output is plain JSX. Adding a props type is one line: type it as React.SVGProps<SVGSVGElement> and the spread is fully typed.
- Is anything sent to a server?
- No. All four transforms run in your browser.