Osy#the first language built for agents
Agents firstAgentic appsWorkflowsDurable Execution — built inSecurityTestingThe editorThe UI modelOne program

Reference / UI

Calling helpers from render

Text(Format(item)) — call a PURE client helper (a component method, a client method/function) from a render expression

A render expression may call a PURE client helper — a component `method`, a client class method, a top-level function — and render the value it returns (`Text(Twice())`). The compiler PROVES the call is safe: client-side, writes no state, invokes no effect. A call that reaches the server, writes state, or runs an effect is a precise compile error, so a render expression stays synchronous and pure by construction.

stable1 example compiled by CIuiauthoringreactivity

Summary#

Render is where you turn data into what's on screen — and sometimes the value you want to show needs a small computation. Write it as a component method (or any pure client helper) and call it right in the render expression:

component Cart() {
  live var items = LineItem.ToList();
  decimal Subtotal() => items.Sum(i => i.Price * i.Qty);   // a pure helper over component state

  render { Text(Subtotal()); }                              // call it in render — natural C#
}

The call runs in the browser, synchronously, as part of rendering — and re-runs (repainting only that slot) when a value the helper read changes. See The reactivity & lifecycle model for the fine-grained update model.

Signature#

render { Text(Helper(arg)) }   // Helper is a component method, a client class method, or a top-level function

A render call is legal iff it is client-side (never suspends) ∧ writes no state ∧ invokes no effect — the compiler checks all three.

Description#

What you can call#

  • A component methodint Twice() => n * 2; then Text(Twice()). It reads the component's own state through this implicitly, exactly as an action does.
  • A client class methodmoney.Formatted() on a class value whose method body is client-runnable.
  • A top-level function whose body is client-runnable — an ordinary string Shout(string s) { … }. You mark nothing: the compiler decides where it can run from what it touches, and refuses the call if the answer is the server.

Pure stdlib calls (Convert.ToString, Enum.Label, …) have always been render-slot material; this extends the same door to your own helpers, so you don't have to hoist a one-line computation into a live var just to name it.

What the compiler refuses — and why#

A render expression must run synchronously and purely on the client (it can re-run many times as data changes, and it may not block or cause side effects). So a call that breaks any of the three rules is a compile error that names the offending reach:

The call…Error
reaches a data read or a server function… cannot be called from a render expression — it reaches server-side work …
writes the component's own state… it assigns the component's own state (count) …
invokes an effect (Log, Http, …)… it invokes the effect Log.Information

The fix is always the same: move the imperative call into an action or on mount, store what it produces in state, and render that. A data read is a live var query (component); a one-time load is on mount / on unmount.

Why this is safe by construction#

The fine-grained renderer re-runs a slot's expression inside a tracking effect whenever a value it read changes (The reactivity & lifecycle model). "A render expression is pure" therefore isn't a convention you must remember — it's a compile-time guarantee, which is what lets you call your helpers in the view without ever creating a render loop.

Examples#

A cart line renders a per-row subtotal via a pure helper — no live var needed for the one-liner:

entity Product { string Name; decimal Price; }

[Page("/catalog")]
[Render(CSR)]
component Catalog() {
  live var products = Product.ToList();
  decimal WithTax(decimal price) => price * 1.25m;   // a pure client helper

  render {
    foreach (var p in products) {
      Text(p.Name);
      Text(WithTax(p.Price));                          // called in render — the compiler proves it pure
    }
  }
}

See also#

  • component — the method member, and the live var query you'd reach for when a value needs the server.
  • The reactivity & lifecycle model — the fine-grained model that makes "render is pure" a structural requirement.
  • on change — the reactive block for pushing a value out of the component (the opposite direction).
  • on mount / on unmounton mount, for one-time imperative setup a render call may not do.

Related

component

The one archetype for all UI: a bounded reactive unit — typed props, reactive members (fields, `live`…

The reactivity & lifecycle model

How an Osy# component comes alive and stays in sync: declarations are live value bindings, `on mount`/`on unmount` are…

on change

`on change { … }` is a reactive **side-effect**: the runtime re-runs it whenever a value it read changes, so it's how…

on mount / on unmount

`on mount { … }` runs a block ONCE, the first time a component appears — before its first paint; `on unmount { … }`…