Template
Template specification
What Template Is
Section titled “What Template Is”Template is the structural description produced by a render function. It tells the runtime/adapter which children should appear inside the current prototype instance’s Root Node, how those children are nested, and which local structures carry only static visual feedback.
Template is not a component instance and not the Root Node. The Root Node is created by the runtime, adapter, or host, and carries component-level channels. The outermost node returned by template is only a child inside that Root Node. This boundary matters: component-level props, event, feedback, expose, context, and similar capabilities do not live on ordinary Template Nodes.
Root Node And TemplateChildren
Section titled “Root Node And TemplateChildren”A render function returns TemplateChildren:
return (r) => [r.el('span', 'Label'), r.el('span', { style: tw('opacity-50') }, 'Hint')];This array expands into sibling children inside the same Root Node. It does not declare two component roots, and the first span does not become the Root Node.
The Root Node anchors component-level channels. An adapter may realize it as a DOM element, a framework component root, a native host node, or another host container, but the template layer only describes its children.
Template Node
Section titled “Template Node”A Template Node is a structural node. It may have children, participate in tree composition, and carry style:
r.el('div', { style: tw('flex flex-col gap-2') }, [ r.el('span', 'Title'), r.el('span', { style: tw('text-muted-foreground') }, 'Description'),]);In v0, ordinary Template Nodes do not directly carry props, state, event, expose, or feedback channel capabilities. They only organize structure inside the Root Node. style is the exception: it activates static feedback-only capability, and its semantics are defined by feedback.style.
Renderer el()
Section titled “Renderer el()”el() is the basic template authoring primitive:
r.el(type);r.el(type, children);r.el(type, props);r.el(type, props, children);The second argument follows fixed dispatch rules: if it is valid TemplateProps, it is interpreted as props; otherwise it is interpreted as children. In the three-argument form, the second argument must be valid TemplateProps.
v0 TemplateProps only permits an empty object or { style?: TemplateStyleHandle }. Template cannot smuggle host attrs, event handlers, prototype refs, or component-level capabilities through the props argument.
Children Normalization
Section titled “Children Normalization”Template children normalize into a portable shape. The default policy is deep flattening with null as empty.
['a', [null, ['b']]] -> ['a', 'b']Top-level undefined canonicalizes to null; null inside arrays is removed; all-empty output canonicalizes to null. Boolean children and child-position undefined throw because they are too easy to interpret differently across hosts.
Normalization only owns children shape. It does not prove every non-null object child is a valid TemplateNode; adapters or renderer boundaries may still validate node types more specifically.
Slot Protocol
Section titled “Slot Protocol”The v0 slot is a reserved template node marking the insertion point for external children:
return (r) => [r.el('button', [r.slot()])];slot must be anonymous, singular, and parameterless. It carries no name, props, params, style, children, or fallback. Richer named slots, scoped slots, or fallback slots belong to upper frameworks or compilation layers, not the core template protocol.
Adapters can rely on this fact when mapping to hosts: a render output has at most one slot, and that slot needs no additional parameter resolution.
No Prototype-Level Composition
Section titled “No Prototype-Level Composition”Proto UI v0 does not provide prototype-level template composition. Normal authoring paths should not produce a PrototypeRef template type; if an official adapter or compiler encounters PrototypeRef in a template, it must actively throw instead of silently supporting it.
This rejection is mostly a conformance signal: adapters should not privately extend the core template protocol by embedding another prototype as an ordinary template node. A substructure that needs independent props, event, expose, context, or state relations should be split into an independent prototype and composed by the host or an upper layer.
Contract Previews
Section titled “Contract Previews”Test Mapping
Section titled “Test Mapping”Template coverage is concentrated in spec/tests/T-TEMPLATE-0001.yaml and implemented through core template tests plus Web Component adapter boundary tests.
| Test entity | Main coverage |
|---|---|
T-TEMPLATE-0001 | root children, node structure, el() dispatch, normalize, slot, PrototypeRef rejection |
These tests turn template from “some host render tree” into a cross-host structural protocol.
Related Specs
Section titled “Related Specs”Coredefines setup returning a render function and render returningTemplateChildren; Template refines the structure of those children.Lifecycledecides when render runs and when commit completes; Template only describes render output.Feedbackdefines the style token semantics for templatestyle; Template does not define a full style system by itself.Props,Event,Expose,Context,State, and similar component-level channels are anchored by the Root Node and their modules, not by ordinary Template Nodes.Prototype Boundaryexplains when a substructure should be split into another prototype; Template only preserves space for feedback-only local structure that does not require splitting.