Developer
JSX Internals
This document explains how @rezi-ui/jsx is wired and how to add new JSX components safely.
This document explains how @rezi-ui/jsx is wired and how to add new JSX components safely.
Architecture
Flow:
- TS/JSX compiler emits calls into
@rezi-ui/jsx/jsx-runtime. createElement()inpackages/jsx/src/createElement.tsresolves:- intrinsic string tags (
"box","statusBar", etc.) viaintrinsicFactories - function components by direct invocation
- intrinsic string tags (
- Intrinsic factories call component functions in
packages/jsx/src/components.ts. - Component functions delegate to
ui.*()factories from@rezi-ui/core. - Core
ui.*()returns final VNodes consumed by commit/layout/render.
Adding a New JSX Component
Checklist:
- Add JSX prop types in
packages/jsx/src/types.ts. - Add component implementation in
packages/jsx/src/components.ts. - Add intrinsic type entry in
ReziIntrinsicElements(types.ts). - Add intrinsic factory mapping in
packages/jsx/src/createElement.ts. - Export component and type(s) from
packages/jsx/src/index.ts. - Add/update tests in
packages/jsx/src/__tests__/. - Update docs:
docs/getting-started/jsx.mddocs/packages/jsx.mdpackages/jsx/README.md
Children Normalization Rules
Implemented in packages/jsx/src/children.ts:
- Container children (
normalizeContainerChildren):- flatten nested arrays
- drop
null,undefined, and booleans - wrap
string/numberasui.text(...)
- Text children (
normalizeTextChildren):- flatten nested arrays
- drop
null,undefined, and booleans - stringify and concatenate remaining values
Key Handling Protocol
- JSX runtime
createElement(type, props, key)injects/overridesprops.keywhen a JSX key is present. - Component-level helper
withOptionalKey()forwardskeyinto core prop objects. - Components that use positional
ui.*()signatures still preserve key by forwarding key in the secondary props object, or by applying key to the returned root VNode when no options object exists.
Why Delegation to ui.*() Is Required
JSX components must call core ui.*() instead of constructing raw VNodes.
Reasons:
- Core applies behavioral transforms (
resolveButtonIntent,resolveBoxPreset, etc.). - Core can evolve (new defaults, normalization, metadata) without JSX drift.
- Delegation guarantees JSX and non-JSX APIs produce equivalent VNode trees.
Testing Strategy
Required coverage for JSX changes:
- Bugfix tests for known transform paths (
Button intent,Box preset). - Composition helper tests for new composite APIs.
- Parity tests that compare JSX output to equivalent
ui.*()calls. - Full suite execution (
node scripts/run-tests.mjs) before merge.
Parity tests are the guardrail that prevents JSX/API divergence over time.