Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/BaselAshraf81/holystitch/llms.txt

Use this file to discover all available pages before exploring further.

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:
export function compileHTMLToJSX(html: string): string

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.
<!-- Input -->
<!DOCTYPE html>
<html lang="en">
  <head><title>App</title></head>
  <body><div class="app">Hello</div></body>
</html>

<!-- After stripping -->
<div class="app">Hello</div>
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*:
<!-- Input -->
<span class="material-symbols-outlined" data-icon="check_circle">check_circle</span>

<!-- After pre-processing -->
<span class="material-symbols-outlined" data-icon="check_circle"></span>
<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:
<!-- Input -->
<pre><code>const x = { a: 1 };</code></pre>

<!-- After pre-processing -->
<pre><code>const x = &#123; a: 1 &#125;;</code></pre>
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:
<!-- section start -->
{/* section start */}
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:
// Before (two root elements — invalid JSX)
<header>...</header>
<main>...</main>

// After
<>
  <header>...</header>
  <main>...</main>
</>

Attribute renaming

All standard HTML attributes that have a different name in JSX are renamed via a static lookup table.
HTML attributeJSX attribute
classclassName
forhtmlFor
tabindextabIndex
readonlyreadOnly
maxlengthmaxLength
minlengthminLength
cellpaddingcellPadding
cellspacingcellSpacing
rowspanrowSpan
colspancolSpan
usemapuseMap
frameborderframeBorder
contenteditablecontentEditable
crossorigincrossOrigin
autocompleteautoComplete
autofocusautoFocus
autoplayautoPlay
enctypeencType
formnovalidateformNoValidate
novalidatenoValidate
http-equivhttpEquiv
accesskeyaccessKey
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:
HTML attributeJSX attribute
onclickonClick
ondblclickonDoubleClick
onchangeonChange
oninputonInput
onsubmitonSubmit
onresetonReset
onfocusonFocus
onbluronBlur
onkeydownonKeyDown
onkeyuponKeyUp
onkeypressonKeyPress
onmousedownonMouseDown
onmouseuponMouseUp
onmouseoveronMouseOver
onmouseoutonMouseOut
onmousemoveonMouseMove
onmouseenteronMouseEnter
onmouseleaveonMouseLeave
onloadonLoad
onerroronError
onscrollonScroll
onwheelonWheel
ondragstartonDragStart
ondragendonDragEnd
ondragoveronDragOver
ondroponDrop
ontouchstartonTouchStart
ontouchendonTouchEnd
ontouchmoveonTouchMove
When an event handler has a value, the raw JavaScript string is wrapped in an arrow function:
<!-- Input -->
<button onclick="handleClick()">Submit</button>
/* Output */
<button onClick={() => { handleClick() }}>Submit</button>

Void element self-closing

Void elements cannot have children and must be self-closed in JSX. The following elements are always emitted with />:
const VOID_ELEMENTS = new Set([
  "area", "base", "br", "col", "embed", "hr", "img", "input",
  "link", "meta", "param", "source", "track", "wbr",
]);
<!-- Input -->
<img src="photo.jpg" alt="Photo">
<br>
<input type="text" name="email">
/* Output */
<img src="photo.jpg" alt="Photo" />
<br />
<input type="text" name="email" />

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-colorbackgroundColor)
  • Pure integer values are emitted as number literals (no quotes)
  • All other values are emitted as quoted strings
  • Double quotes inside values are escaped
<!-- Input -->
<div style="background-color: #fff; padding: 16px; background-image: url('bg.png'); z-index: 10">
/* Output */
<div style={{backgroundColor: "#fff", padding: "16px", backgroundImage: "url('bg.png')", zIndex: 10}}>
Complex values are handled correctly:
<!-- Input -->
<div style="width: calc(100% - 2rem); color: var(--text-primary); font-size: 14px">
/* Output */
<div style={{width: "calc(100% - 2rem)", color: "var(--text-primary)", fontSize: "14px"}}>

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:
<!-- Input -->
<img src="hero.jpg" data-alt="Hero banner showing product dashboard" />
/* Output */
<img src="hero.jpg" alt="Hero banner showing product dashboard" />
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:
<!-- Input -->
<span class="material-symbols-outlined" data-icon="check_circle">check_circle</span>
/* Output */
<span className="material-symbols-outlined"></span>

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:
<!-- Input: two siblings at the root level -->
<header class="sticky top-0 bg-white shadow">...</header>
<main class="flex-1 py-12">...</main>
/* Output */
<>
  <header className="sticky top-0 bg-white shadow">...</header>
  <main className="flex-1 py-12">...</main>
</>

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:
export function requiresClientDirective(jsx: string): boolean {
  return (
    /\bonClick\b|\bonChange\b|\bonSubmit\b|\bonInput\b|\bonFocus\b|\bonBlur\b/.test(jsx) ||
    /\buseState\b|\buseEffect\b|\buseRef\b|\buseContext\b|\buseReducer\b/.test(jsx) ||
    /<button[\s/>]/.test(jsx)
  );
}
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:
<!-- Input HTML -->
<nav class="flex items-center justify-between px-8 py-4 bg-white"
     style="box-shadow: 0 1px 3px rgba(0,0,0,0.1)">
  <img src="logo.svg" data-alt="Company logo" style="width: 120px; height: 40px">
  <ul class="flex gap-6 list-none">
    <li><a href="#" class="text-gray-600 hover:text-gray-900">Features</a></li>
    <li><a href="#" class="text-gray-600 hover:text-gray-900">Pricing</a></li>
  </ul>
  <button class="btn-primary" onclick="openModal()">Get started</button>
  <span class="material-symbols-outlined" data-icon="menu">menu</span>
</nav>
/* Output JSX */
<nav className="flex items-center justify-between px-8 py-4 bg-white"
     style={{boxShadow: "0 1px 3px rgba(0,0,0,0.1)"}}>
  <img src="logo.svg" alt="Company logo" style={{width: "120px", height: "40px"}} />
  <ul className="flex gap-6 list-none">
    <li><a href="#" className="text-gray-600 hover:text-gray-900">Features</a></li>
    <li><a href="#" className="text-gray-600 hover:text-gray-900">Pricing</a></li>
  </ul>
  <button className="btn-primary" onClick={() => { openModal() }}>Get started</button>
  <span className="material-symbols-outlined"></span>
</nav>
Transforms applied:
  • classclassName (three elements)
  • style string → JS object (two elements)
  • data-altalt
  • <img> → self-closed
  • onclickonClick 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

Build docs developers (and LLMs) love