# key: on a component call

> `key:` on a component call names the instance. While the key reads the same the instance stays and takes its new arguments; when it reads differently the old instance goes and a fresh one is built — its fields are initialised again and its `on mount` runs again. Inside a `foreach` the key is also the row's identity, so a re-sorted list moves each row's component with its state. It is Blazor's `@key` and React's `key`.

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

## Summary        {#summary}
A component's fields are initialised **once**, when its instance is made. A call site that later passes a different
argument does not make a new instance — the parameter changes, and the fields keep what they had:

```osy syntax
component NoteEditor(Note note) {
  string text = note.Text;          // read ONCE, when the instance is made
  render { Input(value: text); }
}

NoteEditor(selected);               // pick another note: `note` changes, `text` still holds the first note's draft
NoteEditor(selected, key: selected.Id);   // pick another note: a fresh NoteEditor, `text` is the new note's
```

`key:` says *this is a different one now*. A new key value disposes the instance and builds a fresh one.

## Signature      {#signature}
```osy syntax
<Component>(<args>…, key: <value>)
```

- `<value>` is any ONE value: a row (`key: row`, which is the same as `key: row.Id`), an id, a name, a number, an
  enum member. A list is refused — a key is one value the instance is known by.
- It is read on every render, like any argument. Same value → same instance; different value → fresh instance.
- It is legal on a **component** call only. An atom (`Text`, `Stack`, `Input`) holds no state of its own, so there is
  nothing for a key to reset, and `key:` there is a compile error that says where it belongs.
- A component that declares a parameter called `key` keeps C#'s meaning: `key:` is that parameter — so no caller can
  key that component at all. `osy lint` says so at the declaration (`ui-component-parameter-named-key`, SHOULD): name
  the parameter for what it is the key OF (`invitationKey`, `regionKey`), and leave `key:` to the platform.

## Description    {#description}

### When does a component need a key? {#when}
When it holds **state seeded from a parameter**, and the call site can be handed a different value while it stays on
screen:

| the component holds | the call site | without `key:` |
|---|---|---|
| a draft — `string name = product.Name;` | a detail panel over the selected row | the previous row's draft, shown under the new row |
| something `on mount` loaded for its argument | a pane whose argument is a field the page changes | the old value's data, never reloaded |
| nothing but reads of its parameters | anything | nothing goes wrong — a read of a parameter follows it |

A component that only READS its parameters in `render` (or in a `live var`) needs no key: those reads follow the
argument as it changes. The key is for state the component OWNS.

### What does a new key do? {#reset}
Everything an instance owns starts over: its fields take their initialisers again, its `on mount` runs again, its
`on unmount` runs for the instance that leaves, and its queries are the new instance's. Going BACK to an earlier key
is a fresh instance too — a key names an instance, it is not a cache of the old ones.

### Rows of a foreach {#foreach}
A `foreach` over **rows** (entities) already keys each row by the row, so its components move with their rows when
the list is re-sorted. A `foreach` over plain values — strings, numbers, records — keys its rows by POSITION, so a
re-sort hands each row's component a different item while it keeps its state. Put the key on the component the row
calls, and the key becomes the row's identity:

```osy syntax
foreach (var tag in tags) { TagChip(tag, key: tag); }     // re-sort, insert, filter: each chip keeps its own state
```

### Why is this not the default? {#default}
C# and Blazor do not reset a component when its parameters change, and neither does React: an instance that took
new arguments is the same instance. Resetting on every argument change would throw away state the reader is in the
middle of — a half-typed comment in a card whose `Count` just went up. The key states exactly which argument is the
instance's identity, and nothing else resets.

## Examples       {#examples}
A list of notes beside an editor for the selected one. The editor holds a draft seeded from its note, so the call
site keys it by the note:

```osy title="a detail editor that starts over for each row" test app=ui-component-key
entity Note {
  [Required, MaxLength(200)] string Title;
  [MaxLength(2000)] string Body;
  security { allow read, create, update when IsAuthenticated; }
}

component NoteEditor(Note note) {
  string draft = note.Body;              // seeded ONCE per instance — hence the key at the call site
  bool saved = false;
  action Save() { note.Body = draft; UnitOfWork.Commit(); saved = true; }
  render {
    Stack(gap: 2) {
      Text(note.Title);
      Input(value: draft, placeholder: "Write something");
      Pressable(onClick: Save) { Text("Save"); }
      if (saved) { Text("Saved."); }
    }
  }
}

[Route("/notes")]
component NotesPage() {
  live var notes = Note.OrderBy(n => n.Title).ToList();
  Note? selected = null;
  action Pick(Note n) { selected = n; }
  render {
    Row(gap: 4) {
      Stack(gap: 1) {
        foreach (var n in notes) { Pressable(onClick: () => Pick(n)) { Text(n.Title); } }
      }
      if (selected != null) { NoteEditor(selected!, key: selected!.Id); }
    }
  }
}
```

## See also       {#see-also}
- [component](https://osysharp.com/reference/ui/component/) — fields, parameters and the instance a call site makes.
- [on mount / on unmount](https://osysharp.com/reference/ui/lifecycle/) — `on mount` and `on unmount`, which a new key runs again.
- [Calling helpers from render](https://osysharp.com/reference/ui/render-calls/) — the `foreach` and `if` arms a keyed call sits in.
