Skip to main content
compileHTMLToJSX is a deterministic, regex-based compiler that converts an HTML fragment into valid JSX. It applies a fixed sequence of transforms — no AI, no heuristics. The entry point is:

Transform sequence

1

Strip document wrappers

<!DOCTYPE>, <html>, <head>, and <body> tags are stripped so only the body fragment remains. This runs first so all subsequent transforms work on a clean fragment.
2

Pre-process: Material Symbols and `<pre>` blocks

Two patterns are fixed before attribute rewriting because the regex compiler cannot handle them inline safely.Material Symbols / Icons — Stitch emits icon names as both a data-icon attribute and as text content. The Material Symbols font renders the icon purely via the class name; if the text content is present, the icon name appears as literal text instead of a glyph. The pre-processor strips the text child from any <span> whose class contains material-symbols-* or material-icons*:
<pre><code> blocks — Raw { and } characters inside <pre> blocks are valid HTML but invalid JSX (JSX treats them as expression delimiters). The pre-processor replaces them with HTML entities that render identically in the browser:
3

Transform opening tags

Every opening tag is rewritten: attributes are renamed, event handlers are camelCased, inline styles are converted to objects, and void elements are self-closed.See the detailed attribute tables below.
4

Convert HTML comments to JSX comments

HTML comments are converted to JSX expression comments:
5

Wrap in a fragment if multiple root elements

If the compiled output has more than one root element, a React fragment wrapper is added:

Attribute renaming

All standard HTML attributes that have a different name in JSX are renamed via a static lookup table.
data-* attributes are not remapped — React passes all data-* props through unchanged.

Event handler camelCasing

HTML event handler attributes are renamed to their React camelCase equivalents and wrapped in an arrow function:
When an event handler has a value, the raw JavaScript string is wrapped in an arrow function:

Void element self-closing

Void elements cannot have children and must be self-closed in JSX. The following elements are always emitted with />:

Inline style conversion

CSS style strings are converted to JavaScript objects. The converter handles complex values including url(), calc(), and var() using a balanced-bracket parser — not a naive split on ;. Conversion rules:
  • CSS property names are converted to camelCase (background-color → backgroundColor)
  • Pure integer values are emitted as number literals (no quotes)
  • All other values are emitted as quoted strings
  • Double quotes inside values are escaped
Complex values are handled correctly:

data-alt promotion

Stitch sometimes uses data-alt on <img> elements instead of the standard alt attribute. The compiler promotes it to a proper alt:
If both alt and data-alt are present, data-alt wins (it overwrites the earlier attribute in the ordered map).

data-icon removal

The data-icon attribute is a Stitch artifact used to store the icon name for Material Symbols elements. Because the icon name is already the text content of the element (and the pre-processor strips that text to prevent it from rendering), data-icon serves no purpose in JSX and is dropped:

Fragment wrapping for multiple root elements

When the compiled output has more than one top-level element, it cannot be returned from a React component without a wrapper. The compiler detects this automatically and wraps the output in a <>...</> fragment:

requiresClientDirective

After compilation, the converter checks whether the component’s JSX requires a 'use client' directive in Next.js. The check is a set of regex tests:
A component gets 'use client'; prepended if it contains:
  • Any event handler prop (onClick, onChange, onSubmit, onInput, onFocus, onBlur)
  • Any React hook (useState, useEffect, useRef, useContext, useReducer)
  • Any <button> element — buttons are always interactive and always require the client boundary
requiresClientDirective only applies to Next.js App Router projects. Vite components can use hooks and event handlers freely without any directive.

Before-and-after example

A realistic Stitch component going through the full compiler:
Transforms applied:
  • class → className (three elements)
  • style string → JS object (two elements)
  • data-alt → alt
  • <img> → self-closed
  • onclick → onClick wrapped in arrow function
  • Material Symbols text child stripped; data-icon dropped
  • Since the component contains a <button>, requiresClientDirective returns true and 'use client'; is prepended to the file