# Calling helpers from render

> 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.

<!-- id: ui-render-calls · area: ui · stability: stable · html: https://osysharp.com/reference/ui/render-calls/ -->

## Summary        {#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:

```osy syntax
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](https://osysharp.com/reference/ui/reactivity/) for the fine-grained update model.

## Signature      {#signature}
```osy syntax
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    {#description}

### What you can call   {#allowed}
- A **component `method`** — `int Twice() => n * 2;` then `Text(Twice())`. It reads the component's own state through
  `this` implicitly, exactly as an `action` does.
- A **client class method** — `money.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.

### A helper may read a row's child collection   {#collections}
`decimal Heaviest(Basket b) => b.Fruits.Max(f => f.Weight);` called from render reads the `Fruits` the page LOADED with
the row — the same collection `b.Fruits` reads in the render body itself, including a child added on the page and not
yet saved. On the server the same function runs it as one SQL query, so it serves both.

The compiler makes the row carry it: the collection is added to the `.Include` of the query the row came from — through
a `foreach`, a lambda (`baskets.Sum(b => Heaviest(b))`), a member derived from the query's rows, a reference
(`Heaviest(crate.Basket)`), a component prop, and a helper that hands its parameter on to another. A row that no query
stands behind (a plain `List<T>` you filled yourself) is a compile error naming the path to load.
From an **action**, over a row loaded *without* the collection, the call is answered by the server instead.

A read of the whole entity set (`Fruit.Where(…)`) is still a server read, and still refused here.

### What the compiler refuses — and why   {#refused}
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](https://osysharp.com/reference/ui/component/)); a one-time load is [on mount / on unmount](https://osysharp.com/reference/ui/lifecycle/).

### Why this is safe by construction   {#purity}
The fine-grained renderer re-runs a slot's expression inside a tracking effect whenever a value it read changes
([The reactivity & lifecycle model](https://osysharp.com/reference/ui/reactivity/)). "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       {#examples}
A cart line renders a per-row subtotal via a pure helper — no `live var` needed for the one-liner:

```osy title="render-helper" test app=ui-render-calls
entity Product { string Name; decimal Price; }

[Route("/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
    }
  }
}
```

A helper reading a row's child collection — the page's query does not name `Lines`; the compiler adds it for the helper:

```osy title="render-helper-collection" test app=ui-render-calls-collection
entity Order {
  [Required, MaxLength(40)] string Code;
  [ForeignKey(Order)] OrderLine[] Lines;
}
entity OrderLine {
  [Required] Order Order;
  decimal Amount;
}

decimal Total(Order o) => o.Lines.Sum(l => l.Amount);

[Route("/orders")]
[Render(CSR)]
component Orders() {
  live var orders = Order.OrderBy(o => o.Code).ToList();

  render {
    foreach (var o in orders) {
      Text($"{o.Code}: {Total(o)}");                   // reads o.Lines in the browser, off the loaded row
    }
  }
}
```

## See also       {#see-also}
- [component](https://osysharp.com/reference/ui/component/) — the `method` member, and the `live var` query you'd reach for when a value needs the server.
- [The reactivity & lifecycle model](https://osysharp.com/reference/ui/reactivity/) — the fine-grained model that makes "render is pure" a structural requirement.
- [on change](https://osysharp.com/reference/ui/on-change/) — the reactive block for pushing a value *out* of the component (the opposite direction).
- [on mount / on unmount](https://osysharp.com/reference/ui/lifecycle/) — `on mount`, for one-time imperative setup a render call may not do.
- [action vs. method](https://osysharp.com/reference/ui/action/) — why a `method` (not an `action`) is what you call here: it is the only one of the two that can
  declare a return type.
