Browser-owned input
Drafts stay in the renderer
Section titled “Drafts stay in the renderer”Declare ui.local("form", initialValue) inside the surface and select the field
that an editable control should change. For a form with a numeric temperature:
- TypeScript: use
form.field("temperature").bind()for a control andform.field("temperature").value()in an action payload. - Python: use a typed initial value, such as a
RunInputdataclass, and passform.temperatureas the control’sbind=. Passinput=formto send the whole form orinput={"temperature": form.temperature}to compose the payload. Explicit typed projection usesform.field("temperature", float)followed by.bind()or.value(). - Rust: derive
ActionInputforRunInput, then useform.field(RunInput::field_temperature()).bind()?for a control andform.field(RunInput::field_temperature()).value()in the generatedRunInputInput.
bind() and value() take no pointer argument. Without a field projection,
they refer to the whole Local value. See the SDK reference
for action registration and SDK examples for complete forms.
When the user invokes the action, the renderer resolves expressions into concrete JSON. The application runtime never stores the unfinished draft.
Rerenders and resets
Section titled “Rerenders and resets”A Local’s initial value is applied once. Server rerenders preserve an existing draft. Use resetKey (TypeScript), reset_key (Python), or local_with (Rust) when you explicitly need to reset it. Updating application state alone is not a request to overwrite a draft.
Use stable component keys for controls that appear conditionally. See the SDK examples for a complete form.