React / foldkit-react-codegen

What compiles

FoldkitReact
h.div(attributes, children), any HTML/SVG/MathML tag<div …>…</div>
h.keyed('li')(key, attributes, children)<li key={key} …>
h.emptynull
h.submodel({ model, view, toParentMessage, viewInputs? })view(model, viewInputs?, m => dispatch(toParentMessage(m)))
Submodel.defineView<Model, Message>((model, h) => …)the compiled function, with Model on its parameter
const slot = createLazy(), slot(view, [a, h])view(a, dispatch); the slot is removed
createKeyedLazy() slots, slot(key, view, args)view(...args)
const el = spec.withMessage(h), el([el.Color(c), el.OnColorChanged(f)], [])<color-picker color={c} oncolor-changed={(event: CustomEvent) => dispatch(f(event.detail))} />
Class, Id, Key, For, Tabindex, Readonly, …className, id, key, htmlFor, tabIndex, readOnly, …
AriaLabelledBy(x) and every Aria*aria-labelledby={x}
Attribute('k', v), DataAttribute('k', v)k={v}, data-k={v}
Style({ 'background-color': c })style={{ backgroundColor: c }}
OnClick(message), OnFocus, OnBlur, mouse enter/leave/over/out/down/up/move, OnDoubleClickonClick={() => dispatch(message)}
OnInput(f)onChange={event => dispatch(f(event.currentTarget.value))}
OnKeyDown(f), OnKeyUp(f)onKeyDown={event => dispatch(f(event.key, { shiftKey, ctrlKey, altKey, metaKey }))}
OnKeyDownSelf(f)the same, guarded by event.target === event.currentTarget
OnSubmit(message)onSubmit={event => { event.preventDefault(); dispatch(message) }}

Custom elements work because React 19 writes a prop as a DOM property when the element has that property, and listens for exactly the event name after on. Define the element before React renders it, as Foldkit also needs; otherwise React falls back to an attribute. A spec used only in compiled views is removed; an exported one is kept for other modules. For the output to type-check, the compiler appends a declare module 'react' entry typing each tag's props as Record<string, unknown>; a hand-written declaration for the same tag must use that type.

OnInput becomes React's onChange on purpose. On a text control, React's onChange fires on every native input event, and pairing it with value keeps the input controlled without a warning.