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

Reference / UI

on change

on change { … } — run a block whenever a reactive value it reads changes, to push that value somewhere outside the component

`on change { … }` is a reactive **side-effect**: the runtime re-runs it whenever a value it read changes, so it's how you keep something OUTSIDE the component in sync with something inside it — most commonly the page's title (`on change { Navigation.SetTitle(org.Name); }`). It may not write the component's own state; that's a compile error.

stable1 example compiled by CIuiauthoringreactivity

Summary#

on change { … } runs a block reactively — the runtime re-runs it whenever a value it reads changes. Use it to push a value from the component to somewhere outside it. The canonical case is a page naming its own route:

component OrgEdit(string slug) {
  var org = Organization.Single(o => o.Slug == slug);

  on change { Navigation.SetTitle(org.Name); }     // the tab + browser title track the org's name
}

It belongs to the on <event> lifecycle-event family — on mount (setup, once), on change (a tracked reaction), on unmount (teardown, once). See on mount / on unmount for the once-only siblings and The reactivity & lifecycle model for the whole execution model.

Signature#

on change { <statements> }     // re-runs whenever a reactive value it read changes

on change takes no name — the on <event> family is anonymous. (Don't confuse it with an onChange input prop: Input(value: x, onChange: Handler) is a field's change event, a different thing.)

Description#

What it's for#

An on change block is for outward work — a call whose result lands somewhere the component doesn't own:

You wantUse
A computed value to renderlive var name = expr;
Setup that runs once, when the page openson mount { … } (on mount / on unmount)
Teardown that runs once, when the page closeson unmount { … } (on mount / on unmount)
To keep something outside the component in sync as data changeson change { … }

An on change body has the same powers as an action — it can call server functions and reach the client verbs (Navigation.*, Theme.*). The runtime calls it for you instead of a click.

It may not write its own state#

Assigning the component's own field from an on change block is a compile error:

an on change block must not assign the component's own reactive state (count). That would loop: the write wakes the reaction, which re-runs it.

The rule holds even if the write hides behind an action or method the block calls — the compiler follows the call. If you need a value, use a live var computed; if you need to set state once, use on mount; if a user gesture should set it, use an action.

When it runs#

It runs at mount (to establish its dependencies and do the initial sync), then again whenever any reactive value it read changes — and only then. It is dependency-tracked: reading org.Name subscribes the block to org.Name, so an unrelated change elsewhere on the page does not wake it. This is the whole point of the name — "on change" is tracked-by-construction, where a block "that runs every render" would be waste. Triggers are any reactive read, not only a live var: a plain state field reassigned by an action wakes it too. See The reactivity & lifecycle model.

Writing a body that is idempotent — safe to run again with the same inputs — is the contract; the platform de-duplicates at the sink where it can (calling Navigation.SetTitle with an unchanged title does nothing).

Examples#

A create page whose title tracks the name as you type it — and falls back while the name is still blank:

entity Organization { string Name; }

[Page("/org/new")]
[Title("New organization")]          // the static fallback (server-rendered, and before the reaction first runs)
[Render(CSR)]
component OrgCreate() {
  Organization draft;
  on mount { draft = new Organization {}; }

  live var tabName = draft?.Name ?? "New organization";
  on change { Navigation.SetTitle(tabName); }    // the tab + browser title update as you type

  render {
    Stack(gap: 4) { Input(value: draft.Name, placeholder: "Organization name"); }
  }
}

See also#

  • component — the component on change lives in, and its other members.
  • on mount / on unmounton mount / on unmount, for setup and teardown that run once rather than reactively.
  • The reactivity & lifecycle model — the execution model: declarations vs on mount vs on change vs on unmount, and how a change updates only the slots that read it.
  • NavigationNavigation.SetTitle, and the rest of the route surface an on change block can reach.

Related

component

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

on mount / on unmount

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

The reactivity & lifecycle model

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

Navigation

The routes the user currently has open, and the verbs that move between them. Read `Navigation.Routes` in a layout to…