# on mount / on unmount

> `on mount { … }` runs a block ONCE, the first time a component appears — before its first paint; `on unmount { … }` runs a block ONCE when it goes away — after its children tear down. Both have the full power of an action (set state, create data with `new Entity{}`, call a server function): `on mount` sets a page up, `on unmount` tears it down.

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

## Summary        {#summary}
`on mount { … }` and `on unmount { … }` are component **lifecycle hooks** — the bookends of an instance's life. Each is
an *auto-invoked action*: the runtime calls it for you instead of a click, so each can do everything an action can —
assign a field, create rows with `new Entity{ … }`, call server functions. They belong to the **`on <event>`** family
alongside the reactive [on change](https://osysharp.com/reference/ui/on-change/) block.

- **`on mount`** runs **once, when the component first mounts**, *before its first render*. Its effects are in place
  for the first paint.
- **`on unmount`** runs **once, when the component goes away**, *after its children have torn down*. Use it for a final
  flush or an explicit release.
- **`on frame (double dt)`** runs **once per displayed frame**, for as long as the component is mounted. It is the
  odd one out and the only phase that repeats — see [running code every frame](#frame).

```osy syntax
component OrgCreatePage() {
  Organization draft;                          // no initializer — null until mount
  on mount { draft = new Organization {}; }    // seed a fresh draft to bind the form to
  on unmount { Log.Information("editor closed"); }    // a parting side-effect
  render { Input(value: draft.Name); … }
}
```

## Signature      {#signature}
```osy syntax
on mount   { <statements> }   // runs once, at first mount, before the first render
on unmount { <statements> }   // runs once, at teardown, after children tear down
on frame (double dt) { … }    // runs once per displayed frame; `dt` is the seconds since the last one
T name;                       // a field with NO initializer defaults to null (a bare C# field)
```

Both are **anonymous** (the `on <event>` family carries no name), and a component may declare at most one of each.

## Description    {#description}
Use `on mount` for the setup a page needs the moment it opens, and `on unmount` for the teardown it owes when it leaves:

- **Runs once, per instance.** `on mount` fires when a component instance first mounts; `on unmount` fires when that
  same instance is disposed. Switching to a retained tab and back is the *same* instance, so neither re-fires; a reload
  or reopening the page is a *new* instance, which mounts (and later unmounts) again.
- **`on mount` is before the first paint.** State it sets and rows it creates are reflected in the first render — no
  flash of an empty form.
- **`on unmount` is after the children.** Teardown runs deepest-first: a child region's `on unmount` fires before its
  parent's, and a page's own live queries and reactions are disposed automatically as its scope goes away — you never
  hand-unsubscribe. `on unmount` is for the *extra* teardown only your code knows about.
- **Full action powers.** Read/write a field, `new Entity{ … }` (into the page's overlay), call a server function. It's
  the same body a user action runs.

A companion: a field's **initializer is optional** (D64). `Organization draft;` declares the field with no value (null),
exactly like a C# field — so `on mount` (or an action) can fill it in later. This is what lets an edit form and a create
form look the same: the edit page *loads* its row into a server read, the create page *makes* one in `on mount`, and
both bind their inputs to that one entity.

**Creating vs. editing — the same shape.** Because a created row lives in the page's overlay just like an edited one, a
create page is dirty-tracked exactly like an edit page: a freshly-seeded, untouched draft is **not** dirty (closing the
tab discards nothing and prompts nothing), and it becomes dirty the moment you type. `UnitOfWork.Commit()` on a Save action
persists the draft.

**What these are not (yet).** The lifecycle family is `on mount` / `on unmount` / `on frame`; `on show`/`activate` and
`on route-change` are not built yet. Both run **client-side** (a server-rendered page paints without `on mount`, then
runs it on hydration). A block that reads a server-read field which hasn't loaded yet sees the loading state, the same
as an action would. These are known limits, not bugs.

### Running code every frame — `on frame (double dt)`   {#frame}
`on frame (double dt)` runs **once per displayed frame** — roughly sixty times a second — and is the only lifecycle
phase that repeats and the only one that takes a parameter. It is what a game loop, a simulation or a physics step
is written in, and it is the clock a [Canvas](https://osysharp.com/reference/ui/canvas/) is drawn on.

It is **not a reaction**. [on change](https://osysharp.com/reference/ui/on-change/) re-runs when a value it read changes; a frame body is driven by *time* and
typically reads nothing that changes on its own, so wiring it as a reaction would either never re-run or spin.

```osy syntax
on frame (double dt) {
  elapsed = elapsed + dt;             // `dt` is SECONDS since the previous frame
  x = x + speed * dt;                 // scale by dt, and the motion is frame-rate independent
}
```

Five things about it are worth knowing before you write one:

- **`dt` is required, and it is seconds.** A body that does not scale by elapsed time runs at whatever speed the
  display happens to be — the defining bug of a hand-rolled loop, and invisible on the machine it was written on.
  You name the parameter; `dt` is the convention, not the contract.
- **It is clamped at 100ms.** Return to a backgrounded tab, or hit a long pause, and the real gap can be seconds; an
  unclamped `dt` would teleport everything through walls in one step. Past 100ms the loop runs *slow* rather than
  *wrong* — so a body cannot measure its own frame rate below 10fps from `dt`. Use `DateTime.UtcNow` if you need the
  true elapsed time.
- **It pauses while the tab is hidden**, and the first frame back is an ordinary one rather than one carrying the
  whole absence.
- **It never re-enters.** The next frame is requested only after the body returns, so a slow body slows the loop
  down instead of queueing copies of itself.
- **A body that throws stops the loop, loudly**, and logs the error with its stack. Sixty identical errors a second
  would bury the first one — the only report anybody could act on. The symptom is "it worked, then froze"; read the
  log.

It is **client-side** by construction — it is driven by the browser's frame clock — so a body that reaches a server
function is refused at compile time. A round trip per frame is never what anyone meant.

## Examples       {#examples}
A create form, symmetric with its edit form — `on mount` seeds the draft; the inputs bind to it; Create commits:

```osy title="org-create" test app=ui-lifecycle
entity Organization { string Name; string Slug; }

[Page("/org/new")]
[Render(CSR)]
component OrgCreatePage() {
  Organization draft;
  on mount { draft = new Organization {}; }

  action Create() { UnitOfWork.Commit(); Navigation.Close("/org/new", true); }

  render {
    Stack(gap: 4) {
      Input(value: draft.Name, placeholder: "Organization name");
      Input(value: draft.Slug, placeholder: "team-slug");
      Button("Create", onPress: Create);
    }
  }
}
```

Both bookends on one page — set a title at open, log the close at teardown:

```osy title="mount-and-unmount" test app=ui-lifecycle
[Page("/report")]
[Render(CSR)]
component Report() {
  string range = "";
  on mount { range = "last-30-days"; }     // prime a filter the moment the page opens
  on unmount { Log.Information("report closed"); } // a parting side-effect, after any child region tears down

  render { Text(range); }
}
```

## See also       {#see-also}
- [component](https://osysharp.com/reference/ui/component/) — the component these live in, and its other members (fields, `action`, server reads).
- [on change](https://osysharp.com/reference/ui/on-change/) — the reactive sibling: a block that re-runs *whenever* a value it read changes, not just once.
- [The reactivity & lifecycle model](https://osysharp.com/reference/ui/reactivity/) — the whole execution model: declarations vs `on mount` vs `on change` vs `on unmount`.
- [creating & saving data](https://osysharp.com/reference/ui/data-mutation/) — `new Entity{ … }` and `UnitOfWork.Commit()`, and the two-way `Input(value: entity.Field)` binding.
- [routes and pages](https://osysharp.com/reference/ui/routing/) — binding the page to a route (a create page is just another route).
