Slots
Literal <slot> tags in render() project direct host children into a render-owning RadiantElement. That path is for HTML-first hosts: the consumer writes unordered children in markup, and nothing else will keep reconciling those nodes.
A live node can only have one parent. If a parent JSX tree still owns those children as ranges, host render() plus <slot> moves them and the next parent pass breaks. JSX libraries (including Radiant UI) therefore do not use CE <slot> as the public API.
HTML-first projection
/** @jsxImportSource @ecopages/jsx */
import { RadiantElement, customElement } from '@ecopages/radiant';
@customElement('shell-card')
export class ShellCard extends RadiantElement {
override render() {
return (
<section class="shell-card">
<slot name="heading">
<h2>Fallback heading</h2>
</slot>
<div class="shell-card__body">
<slot>
<p>Fallback body content</p>
</slot>
</div>
</section>
);
}
}<shell-card>
<h2 slot="heading">Projected heading</h2>
<p>Projected body content</p>
</shell-card>Use this when consumers author HTML and will not re-render those children from a parent JSX list or conditional.
JSX libraries: compose the parts
When a view helper owns the shell, put chrome and children in the parent tree. Named helpers (or named props) occupy a region; {children ?? default} fills a default icon or control. The custom element queries data-ref / data-* and paints with @bindTo. It does not render() a <slot> around those children.
<RuiDialog title={<>Edit plant</>}>Body</RuiDialog>
<RuiSelectToggle>
<StarIcon />
</RuiSelectToggle>See RadiantElement and the Radiant UI composition guide.
Semantics (HTML-first)
- Slots are resolved from direct host children.
- The default slot receives direct host children without a
slotattribute. - Named slots receive direct host children whose
slotattribute matches the slotname. - Fallback JSX inside
<slot>renders only when no matching projected content exists. - SSR host rendering serializes the projected content and embeds the metadata needed to reconstruct slot assignments on the client.
Querying Assigned Content
Use @querySlot when component logic needs the assigned elements for a default or named slot on an HTML-first host.