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

Reference / UI

Slot(item) — let the caller decide what each row looks like

foreach (var o in options) { Slot(o); }

`Slot(item)` renders the caller's template once for that item. It is how a component owns the list — the layout, the scrolling, the selection — while the app that uses it owns what a single row looks like.

preview1 example compiled by CIuicomponentslotgeneric

Summary#

A plain Slot places the caller's content once. Slot(item) renders it once per item, with a different value each time — so one component can serve a list of anything.

Signature#

// in the component — pass the item
Slot(<value>);

// at the call site — receive it
SomeComponent(items: xs) { x => … }

Description#

Some components are about a collection: a picker, a list, a table. The component knows how to lay the collection out; only the app knows what one row should say. Slot(item) is what connects those.

The component passes the item:

[Composable] component Picker<T>(T[] options) {
  render {
    Stack {
      foreach (var o in options) { Slot(o); }
    }
  }
}

The caller supplies a template, naming the value it receives:

Picker(options: customers) { c =>
  Row { Text(c.Name); Hint(c.Region); }
}

That block is a template, not content: it runs once per Slot(o), and c is a different customer each time.

The template still sees everything around it. Only the named value comes from the component; the rest of the expression resolves where you wrote it, so a page's own state and the item can appear together:

string search = "";

Picker(options: customers) { c =>
  Text(c.Name, bold: c.Name == search);     // `c` from Picker, `search` from the page
}

A plain Slot is unchanged. A component that just wraps its caller's content — a card, a panel, a dialog — writes Slot; exactly as before. You only need the argument when the same content has to render more than once with different values.

A component may use both. Slot(current) for the closed state of a dropdown and Slot(o) inside its list both render the same template, with different values — which is usually what you want, since the selected row should look like the rows it was chosen from.

If a caller passes plain content to a component that expects a template, nothing renders at that slot — the same as any unfilled slot. Slots are optional by design.

Examples#

A picker that owns the list and lets its caller own the row:

entity Customer { [MaxLength(100)] string Name; }

[Composable] component Picker<T>(T[] options) {
  render {
    Stack {
      foreach (var o in options) { Slot(o); }
    }
  }
}

component Home() {
  live var rows = Customer.ToList();
  render {
    Picker(options: rows) { c =>
      Text(c.Name);
    }
  }
}

See also#

Related

component

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

[Composable] — presentational components in public pages

Mark a presentational, composition-only component `[Composable]` so a public page can compose it without marking it…