# Slot (child content)

> A `Slot` marks where a component renders the content block its caller wrapped around it. Writing `Card { Text("hi"); }` passes `Text("hi")` as Card's children; Card renders them wherever it writes `Slot`. The passed content evaluates in the CALLER's scope, so a wrapper component (a card, a panel, a dialog) can frame arbitrary content without knowing what it is.

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

## Summary        {#summary}
A **`Slot`** is the point where a component renders the **children its caller passed it** — the content projection
mechanism (React's `children`, Vue's `<slot>`). A component that writes `Slot` in its render tree is a **wrapper**:
its caller supplies a content block, and the wrapper decides *where* that content lands.

```osy title="a component with one slot" test app=ui-slots-basic
[Composable] component Card() {
  render { Box { Slot; } }        // whatever the caller wraps in a Card lands here, inside the box
}
```

Calling `Card { Text("hi"); }` renders `Text("hi")` **inside** Card's box. The card frames the content; it never
needs to know what the content is.

## Signature      {#signature}
```osy title="in the wrapper — default, named, and id-bearing slots" syntax
Slot;              // the default slot — renders the caller's children block here
Slot("header");    // a NAMED slot — renders the caller's `slot header { … }` fill here
Slot(id: field);   // …and NAME what lands here, so the wrapper's own caption can point at it
```

A caller passes the default content by writing a **content block** after a component call, and fills a **named**
slot with `slot <name> { … }`:

```osy title="at the call site — fill a named slot or the default" syntax
Card {             // the block below is Card's default children
  slot header {     // fills Card's Slot("header")
    Text("Title");
  }
  Text("body");     // untagged → Card's default Slot
}
```

## Description    {#description}
A component call takes an optional trailing `{ … }` **children block**. Those child nodes are handed to the called
component as its slot content. Wherever that component writes `Slot`, the caller's children render in place.

The projected content evaluates in the **caller's scope**, not the wrapper's. This is what makes wrappers reusable:
the content can read the caller's own state, props, and loop variables — the wrapper only positions it.

```osy title="slot content resolves in the CALLER's scope" test app=ui-slots-scope
[Composable] component Panel() {
  render { Box { Slot; } }
}

[Composable] component Greeting(string who) {
  render {
    Panel { Text(who); }         // `who` is Greeting's prop — resolved in Greeting's scope, not Panel's
  }
}
```

A wrapper that writes `Slot` but whose caller passes **no** children renders nothing at that position (never an
error). A component that never writes `Slot` simply ignores any children a caller passes.

### Named slots   {#named}
A wrapper with more than one insertion point gives each a **name**: `Slot("header")`, `Slot("footer")`. A caller
fills a named slot with a `slot <name> { … }` block; untagged children still fill the default `Slot`.

```osy title="named regions" test app=ui-slots-named
[Composable] component Card() {
  render {
    Box {
      Slot("header");    // the header region
      Slot;              // the default region
    }
  }
}

[Page("/")] [AllowAnonymous] [Render(CSR)]
component Home() {
  render {
    Card {
      slot header { Text("Title"); }   // → Card's Slot("header")
      Text("body");                    // → Card's default Slot
    }
  }
}
```

The slot name is **checked at compile time**: filling a slot the wrapper doesn't declare (a typo, or a slot that
doesn't exist) is a compile error, so a mis-named fill is caught before it ships. An editor completes the available
slot names from the wrapper you're calling. A named slot the caller doesn't fill renders nothing (it's optional).

Slots render identically whether a route is delivered server-side (pre-rendered HTML) or client-side, so a page
built from wrappers hydrates without a flash.

### Naming what lands in a slot   {#labelling}

A wrapper renders the caption and the caller renders the control, on two sides of a boundary. So a form component
looks perfectly labelled and is labelled for exactly one audience: people who can see the layout.

`Slot(id: <handle>)` closes that in the one direction that is well-defined — **the wrapper declares the handle and
claims the content it is filled with.** The caller writes nothing:

```osy title="a Field that names the control it is given" test app=ui-slots-naming
[Composable] component Field(string label) {
  render {
    Stack(gap: 1) {
      Text(label, labelFor: field);   // a real <label>, and it names…
      Slot(id: field);                // …whatever the caller puts here
    }
  }
}

[Page("/signup")] [AllowAnonymous] component SignUp() {
  string name = "";
  render { Field("Your name") { Input(value: name); } }
}
```

The caption is written **once**, at the call site, and the pair is a real `<label for>` — so clicking the words
focuses the field, which on a form of small controls is most of the hit area. Writing `label: "Your name"` on the
`Input` instead gives an accessible name and neither of those: the caption is then written twice, with nothing
keeping the two in step.

⚠ **The fill must be a single element.** `for=` names one element; with two roots there is no answer, and the first
one takes the name. Where a slot legitimately holds several things, name the control directly with `label:`.

## Examples       {#examples}
A reusable card wrapper framing page-specific content:

```osy title="card-wrapper" test app=ui-slots
component Card() {
  render { Box { Slot; } }
}

[Page("/welcome")]
[Render(SSR)]
component Welcome() {
  render {
    Card {
      Text("Welcome");
    }
  }
}
```

A wrapper with a named region plus its default content — the caller fills the named slot by name (a mis-typed name
would be a compile error) and leaves the rest for the default slot:

```osy title="named-slots" test app=ui-slots
component Panel() {
  render {
    Box {
      Slot("header");
      Slot;
    }
  }
}

[Page("/article")]
[Render(SSR)]
component Article() {
  render {
    Panel {
      slot header { Text("Title"); }
      Text("Body");
    }
  }
}
```

## See also       {#see-also}
- [component](https://osysharp.com/reference/ui/component/) — the component archetype a slot lives in
- [layout primitives](https://osysharp.com/reference/ui/layout/) — the layout atoms (`Box`, `Stack`, `Row`) a wrapper frames its slot with
- [routes and pages](https://osysharp.com/reference/ui/routing/) — binding a wrapper-composed page to a route
