Function
A callable prop slot and the functionId + instanceParams binding stored on it.
There is no separate Function entity to create. Function in the product is a callable prop (onClick, onChange, onSubmit, …) plus the call stored on it (functionId + instanceParams). On the published site, a visitor click does not send that binding — the server reloads the call you saved on that element and field.

The call
| Name | Type | Default | Description |
|---|---|---|---|
functionId | string | — | Primitive slug, store action id, or "<hookId>.<method>". |
instanceParams | object | — | Call-site payload. Custom store actions with no declared params ignore it. |
when | {{…}} | always | Evaluated at dispatch. Falsy skips the call (and, for a store action, its whole chain) — not an error. |
A whole-binding string "{{…}}" in instanceParams or when is stored as an expression. Mixed text ("Hello {{stores.user.name}}") stays a string and interpolates.
Bind it with update_props on any event prop:
| Argument | Value |
|---|---|
elementId | The node. |
op | "set" |
key | onClick, onChange, onValueChange, onOpenChange, onSubmit, … |
value | The call table above. |
See Action for primitives, store chains, and hook methods.
One call per element slot
An element event holds one call, matching React's onClick={fn}. Sequences belong on a store action's functions[], pointed at from N slots. A hook config slot (useForm.onSubmit, useEffect.effect / cleanup) is the same shape.
Do not put a JavaScript body on an element. A body is opaque code with a declared signature; it lives as a store action whose chain is a single code-action. The element only stores functionId plus call-site params.
Published dispatch
The published site addresses the slot as element + field (elementId, field). Inner links — store-action steps, onSuccess / onError — have no slot of their own.
The visitor supplies identity and live namespace values. The server loads the call saved on that prop, refuses a missing binding, and runs only a public server primitive. appId (and therefore {{env.X}}) is derived from the element's page, never from the visitor. Component-scoped elements are not on this path.
The other published identity is a hook id — useQuery reloads its saved config and dispatches http-request or supabase-query the same way.
Public server primitives: http-request, supabase-login, supabase-signup, supabase-logout, supabase-query. Bind those directly on the element (or hook) slot. Nested inside a store action they have no published slot and dispatch throws. Chain navigation / set-property with onSuccess on that same top-level binding instead — those continuations run on the client after the server primitive settles.
Client primitives (set-property, navigate, toast, …) never cross that request; they run locally from the call already on the prop.
Wrap vs identity
What the renderer does with the stored call follows the slot, not a second value shape:
| Slot | Declaration | Runtime |
|---|---|---|
Event handler (onClick, onValueChange, …) | identity omitted (default) | Wrap (event) => execute(binding, event, props, slot). Result goes to a store or the next link, not back to the host. |
Host reads the return (filter, itemToStringLabel, validate, …) | identity: true and usually async: false | Pass the compiled store callable as-is. Do not wrap — a wrapper settles to a Promise the host reads as truthy. |
An identity / async: false slot must bind a store action that is a single synchronous code-action. A primitive has no compiled callable; binding set-property there is rejected. Hook methods are always wrapped as handlers, even on an identity slot.
Absent params on the slot means React's single event argument. Declared params on a wrapped event field are type-level today; {{event}} is still the stored spelling for that argument.
Hook and effect slots
These config keys are also function slots — persist a call, not a body:
| Hook | Slot | {{event}} |
|---|---|---|
useForm | onSubmit | Submitted form values after validation ({{event.value}}). |
useEffect | effect / cleanup | Cleanup is where cancellation composes (abort a held controller), matching React's return () => …. |
update_hook(config) wholesale-replaces the config object — list_hooks first, then send fields and onSubmit together.