CodelabDocs
Behavior

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.

Button selected in the explorer with its function slot in the props inspector
Button selected in the explorer with its function slot in the props inspector

The call

NameTypeDefaultDescription
functionIdstringPrimitive slug, store action id, or "<hookId>.<method>".
instanceParamsobjectCall-site payload. Custom store actions with no declared params ignore it.
when{{…}}alwaysEvaluated 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:

ArgumentValue
elementIdThe node.
op"set"
keyonClick, onChange, onValueChange, onOpenChange, onSubmit, …
valueThe 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 iduseQuery 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:

SlotDeclarationRuntime
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: falsePass 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:

HookSlot{{event}}
useFormonSubmitSubmitted form values after validation ({{event.value}}).
useEffecteffect / cleanupCleanup 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.

On this page