A slot is a placeholder a child component declares so a parent can pour markup into it. It’s how LWC composes components without the child knowing exactly what content it will receive — the same Web Components <slot> element you’d use in vanilla DOM.
The default slot
<!-- card.html (child) -->
<template>
<div class="card">
<header>{title}</header>
<div class="body">
<slot></slot> <!-- default slot -->
</div>
</div>
</template>
<!-- usage.html (parent) -->
<template>
<c-card title="Account Details">
<p>Anything I put here ends up in the default slot.</p>
<lightning-button label="Edit"></lightning-button>
</c-card>
</template>
The child renders its frame; the parent provides the content. The child has no idea what’s coming.
Named slots
When a card needs a header, body, and footer, declare multiple slots and name them:
<!-- panel.html -->
<template>
<section class="panel">
<header><slot name="header"></slot></header>
<main><slot></slot></main> <!-- default -->
<footer><slot name="footer"></slot></footer>
</section>
</template>
<!-- usage.html -->
<template>
<c-panel>
<h2 slot="header">Open Cases</h2>
<ul>
<li>Case #1234</li>
<li>Case #1235</li>
</ul>
<lightning-button slot="footer" label="View all"></lightning-button>
</c-panel>
</template>
The parent uses the standard HTML slot="name" attribute. Anything without a slot attribute lands in the default (unnamed) slot.
Fallback content
You can give a slot default content that renders only when the parent provides nothing:
<slot name="footer">
<small>No actions configured.</small>
</slot>
Useful for empty states and progressive enhancement.
Detecting slot content
Sometimes the child needs to know whether a slot was filled — to add a divider, change layout, or skip a wrapper. Use the slotchange event:
<template>
<slot name="footer" onslotchange={handleFooterChange}></slot>
</template>
handleFooterChange(event) {
const slot = event.target;
const assigned = slot.assignedNodes({ flatten: true });
this.hasFooter = assigned.length > 0;
}
Scoped slots (light DOM only)
A scoped slot lets the child pass data down into the parent’s slot template. Think list components that own iteration but let the parent control row rendering:
<!-- list.html (child, with light DOM) -->
<template lwc:render-mode="light">
<template for:each={rows} for:item="row">
<slot lwc:slot-bind={row} key={row.id} name="row"></slot>
</template>
</template>
<!-- usage.html (parent) -->
<template>
<c-list rows={contacts}>
<template lwc:slot-data="row" slot="row">
<strong>{row.Name}</strong> — {row.Email}
</template>
</c-list>
</template>
Two important constraints:
- Scoped slots only work in light DOM (
lwc:render-mode="light"). - The parent declares the binding name with
lwc:slot-data="row"; that’s the variable the parent’s slot template uses.
Shadow DOM and slot styling
Slotted content lives in the parent’s DOM tree, not the child’s. That has two consequences:
-
CSS in the child cannot directly style slotted content — except via the
::slotted()selector, which is itself limited to direct children:/* Inside card.css */ ::slotted(*) { margin: 0.5rem 0; } ::slotted(button) { padding: 0.25rem 0.75rem; } -
Slotted content uses the parent’s styles, not the child’s. That’s a feature, not a bug — it keeps shadow encapsulation clean.
Common interview follow-ups
- “How is
<slot>different from@api?” —@apipasses structured data (props);<slot>passes markup. Use props for data, slots for layout flexibility. - “What’s
lwc:render-mode='light'?” — opts the component out of Shadow DOM. Slotted content is then a full DOM citizen — important for SEO, third-party libraries, and scoped slots. - “Why doesn’t my CSS reach into the slot?” — Shadow DOM isolation. Either use
::slotted(), switch to light DOM, or expose CSS custom properties for the parent to set.
When to use slots vs props
| You want to pass… | Use |
|---|---|
| A string, number, or data object | @api property |
| Layout / markup the child should host | <slot> |
| A list of items the child should iterate | property + <slot> for each row template (scoped slot) |
| A callback function | event (don’t pass functions as props in LWC) |
Verified against: LWC Developer Guide — Pass Markup into Slots. Last reviewed 2026-05-17 for Spring ‘26 release.