Slots in LWC – Default & Named Slots
Learn how to use default slots and named slots in Lightning Web Components to build flexible, reusable child components that accept dynamic content from parent components — a core pattern for composable LWC architecture.
1 Introduction to Slots
A slot is a placeholder inside a child component's template that a parent component can fill with its own HTML content. This is the standard Web Components composition model — LWC implements it natively.
Think of a slot as a "hole" in a component's template. The child component defines where the hole is; the parent component decides what to put in it.
LWC supports two types of slots:
• Default slot — A single unnamed slot that catches any content passed without a slot attribute.
• Named slot — A slot with a specific name attribute, allowing the parent to target exactly which slot receives which content.
2 Default Slot — Basic Example
A default slot is declared in the child component using a bare <slot></slot> tag with no name attribute. Any content placed inside the child component tag in the parent will flow into this slot.
<slot></slot> — place this where you want parent content to appear.
<template>
<div class="card-wrapper"
style="border:1px solid #0070D2; border-radius:10px;
padding:16px; background:#fff;">
<h3 style="color:#032D60; margin-bottom:12px;">
📦 My Card Component
</h3>
<!-- Default slot: parent content goes here -->
<slot></slot>
</div>
</template>
<template>
<lightning-card title="Default Slot Demo" icon-name="utility:insert_template">
<div class="slds-p-around_medium">
<c-my-card>
<!-- This content fills the default slot in c-my-card -->
<p>Hello! I am content passed from the parent.</p>
<p style="color:green;">✅ I appear inside the card component.</p>
</c-my-card>
</div>
</lightning-card>
</template>
import { LightningElement } from 'lwc';
export default class MyCard extends LightningElement {
// No special JS needed — slots are purely template-level
}
• The <slot></slot> tag in the child acts as a placeholder — the actual DOM is injected by the parent at runtime.
• The parent passes any HTML elements (paragraphs, buttons, icons, etc.) between the <c-my-card> tags.
• No JavaScript is needed in the child to enable slotting — it is purely a template feature.
3 Default Slot Fallback Content
You can provide fallback content inside a <slot> tag. This fallback is rendered only when the parent passes no content into that slot. It acts as a default value.
<template>
<div style="border-left:4px solid #0070D2; background:#eff6ff;
padding:12px 16px; border-radius:0 8px 8px 0;">
<slot>
<!-- Fallback: shown only if parent passes no content -->
<p style="color:#64748b;">
ℹ️ No custom message provided. This is the default content.
</p>
</slot>
</div>
</template>
<template>
<div class="slds-p-around_medium">
<!-- Custom content provided → fallback is NOT shown -->
<c-alert-box>
<p>🚨 Critical: Please review the pending approvals immediately!</p>
</c-alert-box>
<br/>
<!-- No content provided → fallback IS shown -->
<c-alert-box></c-alert-box>
</div>
</template>
• The first usage passes custom content — the fallback is replaced entirely.
• The second usage passes nothing — the fallback paragraph renders automatically.
• Fallback content is a great pattern for providing a sensible "empty state" in reusable components.
4 Named Slots — Syntax & Basic Example
Named slots allow a child component to define multiple distinct injection points. The parent uses the slot="slotName" attribute on its content elements to target a specific slot.
This is the pattern used to build layout components like cards with separate header, body, and footer regions.
<slot name="header"></slot>📌 Parent syntax:
<p slot="header">Content</p>
<template>
<div style="border:1px solid #cbd5e1; border-radius:10px;
overflow:hidden; font-family:sans-serif;">
<!-- Named slot: header -->
<div style="background:#032D60; color:#fff;
padding:12px 16px; font-weight:700;">
<slot name="header">Default Header</slot>
</div>
<!-- Named slot: body -->
<div style="padding:16px; background:#fff;">
<slot name="body">
<p style="color:#94a3b8;">No body content provided.</p>
</slot>
</div>
<!-- Named slot: footer -->
<div style="background:#f8fafc; padding:10px 16px;
border-top:1px solid #e2e8f0;">
<slot name="footer">Default Footer</slot>
</div>
</div>
</template>
<template>
<lightning-card title="Named Slots Demo" icon-name="utility:layout">
<div class="slds-p-around_medium">
<c-panel-layout>
<!-- Targets the "header" named slot -->
<span slot="header">📊 Q3 Sales Report</span>
<!-- Targets the "body" named slot -->
<div slot="body">
<p>Total Revenue: <b>$1,250,000</b></p>
<p>Units Sold: <b>4,320</b></p>
<p>Top Region: <b>North America</b></p>
</div>
<!-- Targets the "footer" named slot -->
<span slot="footer">Last updated: March 2026</span>
</c-panel-layout>
</div>
</lightning-card>
</template>
• The child defines three named slots: header, body, and footer.
• The parent targets each slot using slot="slotName" on the content element.
• Content without a slot attribute goes into the default (unnamed) slot, if one exists.
• Named slots can also have fallback content, shown when the parent provides nothing for that slot.
5 Practical Example — Reusable Modal Component
A classic real-world pattern: a reusable modal dialog where the title, body content, and action buttons are all injected by the parent through named slots. The modal shell is reused across the app — only the content changes.
<template>
<template lwc:if={isOpen}>
<!-- Backdrop -->
<div style="position:fixed; inset:0; background:rgba(0,0,0,0.5);
z-index:1000; display:flex; align-items:center;
justify-content:center;">
<!-- Modal Box -->
<div style="background:#fff; border-radius:12px; width:480px;
max-width:90vw; box-shadow:0 20px 60px rgba(0,0,0,0.3);">
<!-- Modal Header (named slot) -->
<div style="padding:16px 20px; border-bottom:1px solid #e2e8f0;
display:flex; justify-content:space-between;
align-items:center;">
<h2 style="font-size:18px; color:#032D60;">
<slot name="modal-header">Modal Title</slot>
</h2>
<lightning-button-icon
icon-name="utility:close"
onclick={closeModal}>
</lightning-button-icon>
</div>
<!-- Modal Body (named slot) -->
<div style="padding:20px;">
<slot name="modal-body">
<p style="color:#94a3b8;">No content provided.</p>
</slot>
</div>
<!-- Modal Footer (named slot) -->
<div style="padding:12px 20px; border-top:1px solid #e2e8f0;
display:flex; justify-content:flex-end; gap:10px;">
<slot name="modal-footer">
<lightning-button label="Close" onclick={closeModal}>
</lightning-button>
</slot>
</div>
</div>
</div>
</template>
</template>
import { LightningElement, api } from 'lwc';
export default class ModalShell extends LightningElement {
@api isOpen = false;
closeModal() {
this.dispatchEvent(new CustomEvent('close'));
}
}
<template>
<lightning-card title="Contact Form" icon-name="utility:contact">
<div class="slds-p-around_medium">
<lightning-button
label="Open Delete Confirmation"
onclick={openModal}>
</lightning-button>
<c-modal-shell is-open={showModal} onclose={closeModal}>
<!-- Fill the modal-header slot -->
<span slot="modal-header">🗑️ Confirm Delete</span>
<!-- Fill the modal-body slot -->
<div slot="modal-body">
<p>Are you sure you want to delete <b>John Doe</b>?</p>
<p style="color:#ef4444; margin-top:8px;">
⚠️ This action cannot be undone.
</p>
</div>
<!-- Fill the modal-footer slot -->
<div slot="modal-footer">
<lightning-button
label="Cancel"
onclick={closeModal}
class="slds-m-right_small">
</lightning-button>
<lightning-button
label="Delete"
variant="destructive"
onclick={handleDelete}>
</lightning-button>
</div>
</c-modal-shell>
</div>
</lightning-card>
</template>
import { LightningElement } from 'lwc';
export default class ContactForm extends LightningElement {
showModal = false;
openModal() { this.showModal = true; }
closeModal() { this.showModal = false; }
handleDelete() {
// delete logic here
this.closeModal();
}
}
• The c-modal-shell component is fully reusable — it knows nothing about the content inside it.
• Each usage of the modal can have completely different header, body, and footer content.
• The onclose custom event bubbles from the child's close button up to the parent to dismiss the modal.
• The @api isOpen property controls visibility from the parent — a clean separation of concerns.
6 Default + Named Slots Together
A child component can have both a default slot and named slots simultaneously. The default slot catches any content that doesn't have a slot attribute.
<template>
<div style="border:1px solid #e2e8f0; border-radius:10px; overflow:hidden;">
<!-- Named slot: icon area on the left -->
<div style="float:left; padding:16px; background:#f1f5f9;">
<slot name="icon">
<span style="font-size:24px;">📋</span>
</slot>
</div>
<!-- Default slot: main text content -->
<div style="padding:16px; overflow:hidden;">
<slot>
<p style="color:#94a3b8;">No content provided.</p>
</slot>
</div>
<!-- Named slot: action button area -->
<div style="clear:both; padding:10px 16px;
border-top:1px solid #e2e8f0; text-align:right;">
<slot name="action"></slot>
</div>
</div>
</template>
<template>
<lightning-card title="Mixed Slots Demo" icon-name="utility:notification">
<div class="slds-p-around_medium">
<c-info-panel>
<!-- Targets named slot "icon" -->
<span slot="icon" style="font-size:28px;">🔔</span>
<!-- No slot attribute → goes to the DEFAULT slot -->
<h4 style="margin:0 0 4px; color:#032D60;">New Assignment</h4>
<p style="color:#475569;">
You have been assigned to the "Salesforce Migration" project.
</p>
<!-- Targets named slot "action" -->
<lightning-button
slot="action"
label="View Details"
variant="brand">
</lightning-button>
</c-info-panel>
</div>
</lightning-card>
</template>
• The <span slot="icon"> targets the named icon slot on the left.
• The <h4> and <p> have no slot attribute — they go into the default slot automatically.
• The <lightning-button slot="action"> targets the action named slot at the bottom.
• This pattern creates highly composable layout components with zero hard-coded content.
7 Default Slot vs Named Slot — Comparison
| Aspect | Default Slot | Named Slot |
|---|---|---|
| Child syntax | <slot></slot> |
<slot name="header"></slot> |
| Parent targets it via | Any content without a slot attribute |
slot="header" attribute on the element |
| How many per component | Only one default slot allowed | Multiple named slots allowed |
| Fallback content | ✅ Supported — content inside <slot> tags |
✅ Supported — content inside <slot name="x"> tags |
| Use case | Single content injection point (wrapping components) | Multiple distinct regions (header, body, footer) |
| Typical components | Wrappers, containers, alert boxes | Modals, cards, panels, page layouts |
8 Key Rules & Best Practices
| Rule | Detail |
|---|---|
| Child defines, parent fills | Only the child component declares <slot> tags. The parent is responsible for providing content to fill them. |
| Only one default slot | A component can have at most one unnamed (default) slot. Use named slots when you need multiple injection points. |
| Named slots can be empty | If the parent doesn't pass content for a named slot, the slot is simply empty (or shows fallback if defined). No error is thrown. |
| Slot content is styled by parent | The slotted content belongs to the parent's light DOM — it is styled by the parent's CSS, not the child's. The child cannot style slot content directly. |
| Use @api for configuration, slots for content | Pass simple values (strings, booleans) via @api properties. Use slots for rich HTML content that varies between usages. |
| Slots don't add DOM elements | The <slot> tag itself is not rendered as a DOM element — it is replaced by the passed content in the final output. |
| Avoid deeply nested slots | Passing content through multiple levels of slot composition (grandparent → parent → child) can make components hard to debug. Keep slot chains shallow. |
9 Quick Reference
Default slot (child defines):
<!-- In child component template -->
<template>
<div class="wrapper">
<slot></slot> <!-- default slot, no name -->
</div>
</template>
Default slot with fallback (child defines):
<slot>
<p>This renders if parent provides no content.</p>
</slot>
Named slots (child defines):
<template>
<header><slot name="header">Default Header</slot></header>
<main> <slot name="body">Default Body</slot> </main>
<footer><slot name="footer">Default Footer</slot></footer>
</template>
Parent filling named slots:
<c-my-component>
<span slot="header">My Custom Header</span>
<div slot="body">My Custom Body Content</div>
<p slot="footer">My Custom Footer</p>
<span>This goes to the default slot (no slot attr)</span>
</c-my-component>
<slot name="x">. The parent fills them with slot="x". Use default slots for single-region wrappers, named slots for multi-region layout components like modals, cards, and panels. Slot content is styled by the parent, not the child.