The shape of the per-field props passed to render
functions and widgets (e.g. RenderProps for React,
HtmlRenderProps for HTML).
The type each render function and widget emits
for a single field (e.g. unknown / ReactNode for React,
string for HTML).
The resolver shape that maps schema types to
render functions (e.g. ComponentResolver for React,
HtmlResolver for HTML).
Build the per-field props handed to the render function or widget
when it is about to be invoked. Called at most once per dispatch
— adapters that need the same props for both the widget lookup
and the resolver lookup may call it twice through the
dispatchRenderField boundary.
Look up a render function for tree.type in the resolver. Each
adapter wires this to its own getRenderFunction /
getHtmlRenderFn lookup so the dispatcher does not need to know
which resolver shape applies.
The returned render function's output is typed unknown rather
than Output so adapters whose render functions historically
returned a broader type (React's
RenderFunction\<unknown, RenderProps\>) compose naturally. The
dispatcher hands the unknown return value to
DispatchConfig.coerceResult, which narrows it to
Output once per dispatch.
Produce the output emitted when the dispatcher hits
MAX_RENDER_DEPTH. Adapters return their own sentinel
(React: a <fieldset> element; HTML: the recursionSentinelHtml
string; etc.) so the caller decides how to mark recursive
positions in the rendered output.
Produce the output emitted when no widget or resolver render
function handled the field. Most adapters either return a
<span> of the stringified value (React) or throw — the
dispatcher does not interpret the return value, only forwards
it.
Coerce the raw unknown return value of a render function or
widget into the adapter's Output type, or undefined if the
result should be discarded (so the dispatcher falls through to
the next step).
The step argument identifies which dispatch stage produced
the result — "widget" for a .meta({ component }) match,
"resolver" for the per-type render function. The two cases
historically differed in how they treated null /
undefined returns (widget falls through; resolver
short-circuits with null so empty-array suppressions render
nothing), and adapters can preserve that asymmetry by
branching on step.
Each adapter applies its own validity check here — React
narrows via isValidElement/string/number, HTML treats every
string as valid, etc. Returning undefined makes the
dispatcher behave as if no renderer produced output.
OptionallookupOptional widget-lookup hook. When present, the dispatcher
consults it before the resolver lookup. Called once per
dispatch with the value of tree.meta.component; should
return the registered render function or undefined if no
widget matches. The returned function's output type matches
the resolver lookup (unknown) — see
DispatchConfig.lookupRenderFn.
OptionalwrapWrap a render-time error in a SchemaRenderError (or a caller-specified subclass) so every adapter routes thrown errors through the same structured path. Called only for errors thrown by the resolver render function — widget errors propagate without wrapping, matching the historic React behaviour where widgets are user code at the application boundary.
Per-adapter configuration consumed by dispatchRenderField.
Each adapter (React, HTML, future Vue / Solid / Svelte / Lit) supplies one of these to plug its own per-field-props shape, output type, and fallback/error behaviour into the shared dispatch loop without having the dispatcher hardcode any framework-specific imports.