Props
Props specification
What Props Is
Section titled “What Props Is”Props is one of Proto UI’s information channels. It carries configuration input from the App Maker to the Component, letting the caller tell the component which parameters to use for this invocation.
Without the Props channel, the App Maker has almost no stable way to actively send data into a component. A component could still own internal state, respond to user events, and expose capabilities, but the external caller could not configure it in a portable way.
This channel is not a simple shared name for React props, Vue props, or Web Component attributes. Different hosts may collect input differently, but once that input enters Proto UI, Props follows one declaration, resolution, read, and watch model.
Execution Phases
Section titled “Execution Phases”Props APIs are split across setup and runtime.
setup describes the component’s configurable surface: prop shapes, defaults, and watchers. Real invocation input does not exist yet, so setup code cannot read the props passed to a concrete call.
runtime reads the props for the current invocation. At that point the component is already running, so it cannot introduce new prop shapes or register new watchers. Proto UI forbids that kind of structural side effect during runtime.
The model is:
raw props -> declared prop shape -> resolution -> resolved props -> runtime read / watcher callbackDefining Props
Section titled “Defining Props”def.props.define() declares which props a component accepts and how each key should be interpreted. It defines the configurable surface of the component; it does not write runtime data into the component.
type TooltipProps = { disabled: boolean; side: 'top' | 'right' | 'bottom' | 'left'; delay: number;};
def.props.define<TooltipProps>({ disabled: { type: 'boolean', default: false }, side: { type: 'enum', options: ['top', 'right', 'bottom', 'left'], default: 'bottom', }, delay: { type: 'number', range: { min: 0, max: 1000 }, default: 100 },});The same prototype may call define() more than once. These declarations are merged rather than simply replaced by the latest call. Compatible declarations form the final prop shape together; incompatible declarations are rejected and throw. See C-PROPS-0007 for the detailed merge rules.
Props values only support JSON values. string, number, boolean, null, arrays, and plain objects can be portable props values; functions, dates, regular expressions, DOM objects, class instances, and similar host objects cannot. default values must follow the same JSON value boundary.
Watching Props Changes
Section titled “Watching Props Changes”def.props.watch() registers a watcher for props changes. It observes resolved props changes, not every raw props delivery.
def.props.watch(['disabled'], (run, next, prev, info) => { if (next.disabled !== prev.disabled) { run.update(); }});Passing props again does not automatically update the UI. It updates the Props channel and triggers watchers only when relevant values actually change. Whether a render commit happens belongs to higher-level runtime/update semantics; Props does not imply it on its own.
Watcher triggering is key-granular. Passing the same props twice should not trigger a callback. A change to key a does not trigger a callback that watches only key b. A watcher may observe multiple keys; a change to any watched key triggers that watcher.
def.props.watchAll() observes changes across all declared props. Initial hydration does not trigger props watchers; watchers respond only to later changes.
Reading Props Values
Section titled “Reading Props Values”run.props.get() returns the resolved props snapshot for the current runtime invocation. Resolved props are the result of declaration and resolution:
- They contain only declared keys.
- Undeclared keys passed by the caller are dropped.
- They never expose
undefined. - Empty results use
null. - Invalid values are handled by that key’s
empty/ fallback rules.
const props = run.props.get();
if (!props.disabled) { // Run behavior from the current invocation's resolved props.}run.props.isProvided(key) tells whether the key was explicitly present in the current raw props. It answers “did the caller pass this key?”, not “is the resolved value non-empty?”.
Raw props and resolved props should both be treated as immutable snapshots. Mutating these objects has no protocol meaning and should not be used to communicate state changes.
Raw Props APIs For Diagnostics
Section titled “Raw Props APIs For Diagnostics”run.props.getRaw(), def.props.watchRaw(), and def.props.watchRawAll() are raw props escape hatches. They expose the pre-resolution snapshot submitted by the adapter to the Props module, and are appropriate for diagnostics, debugging, host compatibility work, or rare escape-hatch cases.
Raw props are not guaranteed to be the absolute untouched host input. React, Vue, and Web Components split props, attrs, listeners, and DOM attributes differently, and adapters may decide which host inputs enter raw props.
Raw APIs do not extend portable props semantics. Raw props may contain undeclared keys and host-related values; relying on them weakens cross-platform compatibility. Official prototype logic should prefer resolved props.
Contract Previews
Section titled “Contract Previews”Test Mapping
Section titled “Test Mapping”Props coverage is mapped through spec/tests/T-PROPS-0001..0011.yaml and implemented across module, runtime, and adapter conformance tests.
| Test entity | Main coverage |
|---|---|
T-PROPS-0001 | JSON props value boundary |
T-PROPS-0004 | Declaration merge safety and failed merge atomicity |
T-PROPS-0005 | Resolved snapshot shape |
T-PROPS-0006 | Empty / invalid fallback resolution |
T-PROPS-0007 | Resolved watcher diff, coalescing, and dispatch order |
T-PROPS-0008 | Raw props escape hatch and raw watcher behavior |
T-PROPS-0011 | applyRawProps runtime/controller integration |
These tests are not just implementation regression checks; they turn the YAML contract entities into executable behavior boundaries.
Related Specs
Section titled “Related Specs”Coredefines shared channel, value, and phase foundations; Props is one concrete channel.Staterepresents internal continuity over time; Props represents external configuration input.Eventrepresents user-to-component interaction input; Props represents maker-to-component configuration input.Contextfits shared environment between components; Props fits explicit local configuration from the caller.- runtime/update still needs to own positive scheduling for
run.update()and render commit;C-PROPS-0014only states that props updates do not imply commit by themselves.