inside the content a caller passes to a named slot is evaluated at slot-expansion time against an empty context, and the result is written into the output as an empty string. The expression is gone - there is nothing left for the client to re-evaluate.
Attribute bindings in the same slot content survive intact, which is what makes this quiet rather than obvious: half the slot works.
Reproduction
Probe.stx, a component whose slot is rendered inside a client-side loop:
<script client>
const rows = state([{ name: 'alpha' }])
</script>
<template :for="row in rows()">
<div>
<slot name="cell" :row="row" />
<span class="component-own">{{ row.name }}</span>
</div>
</template>Caller:
<Probe>
<template #cell="{ row }">
<em class="a">A:{{ row.name }}</em>
<em class="b">B:{{ callerSignal() }}</em>
<em class="c" :title="row.name" x-text="row.name"></em>
</template>
</Probe>Compiled with compileTemplate(file, '/probe'), the output contains:
<em class="a">A:</em>
<em class="b">B:</em>
<em class="c" :title="row.name" x-text="row.name"></em>
<span class="component-own">{{ row.name }}</span>So, in slot content:
| form | survives |
|---|---|
{{ slotProp.x }} | no, emptied |
{{ callerOwnSignal() }} | no, emptied |
{{ callerOwnExpr }} | no, emptied |
:attr="slotProp.x" | yes |
x-text="slotProp.x" | yes |
@click="handler(slotProp)" | yes |
The component's own {{ row.name }}, in the same loop, is left as a client expression and works. Only content that arrived through a slot is affected.
It is not specific to slot props: a caller's own signal is emptied too, so this is about slot content being interpolated eagerly rather than about the loop variable being unavailable.
Why it matters
It is silent. Nothing warns, the markup renders, the element is there, and only the text is missing - so it reads as a styling or data problem rather than a template one.
Found while adopting a 729-line data-table component in Stacks that has cell, actions and footer slots. It had zero callers after all this time, and this is why: the slots look usable, and any a caller puts in them disappears. See stacksjs/stacks#2924.
Expected
Either leave slot content's for the client, the way the component's own expressions are left, or fail loudly when a slot expression cannot be resolved at expansion time. Silently writing an empty string is the one outcome that cannot be debugged from the output.
Related: the typechecker does not know scoped slot props either, filed separately.
Verified on 0.2.605 (packages/stx, fa8a407bfa) and on the 0.2.534 build installed in the Stacks checkout.