LEARNING OBJECTIVES ⌵
- Implement multi-slot component layouts using named
<slot name="...">elements. - Bind consumer Light DOM elements to specific Shadow DOM viewports using the
slotattribute. - Understand the browser's slot resolution rules for multiple elements assigned to the same slot name.
- Handle unassigned slot attributes and duplicate shadow slot names according to the WHATWG specification.
📖 The Mental Model & Story (Intuitive Foundation)
Imagine an international shipping terminal with a giant cargo airplane. The airplane fuselage is carefully divided into specialized compartments:
- The Upper Cockpit (designated for flight crew and navigation instruments).
- The Climate-Controlled Mid-Deck (designated for perishable agricultural goods).
- The Lower Cargo Hold (designated for heavy bulk containers).
+-------------------------------------------------------------------------------+
| CARGO AIRPLANE COMPARTMENTS |
| |
| +-------------------------- FUSELAGE (Shadow DOM) ----------------------+ |
| | [ Cockpit Bay ] --> <slot name="flight-deck"> | |
| | [ Climate Cargo Bay ] --> <slot name="perishables"> | |
| | [ General Cargo Hold] --> <slot> (Default catch-all) | |
| | [ Heavy Cargo Bay ] --> <slot name="heavy-hold"> | |
| +-----------------------------------------------------------------------+ |
| ^ |
| | Routed by tag label |
| +---------------------------------+-------------------------------------+ |
| | CARGO PACKAGES (Light DOM Elements): | |
| | - <div slot="flight-deck">Captain Rivera</div> | |
| | - <div slot="perishables">Strawberries</div> | |
| | - <div slot="heavy-hold">Steel Coils</div> | |
| | - <div>Standard Suitcases</div> <-- (Goes to default catch-all slot)| |
| +-----------------------------------------------------------------------+ |
+-------------------------------------------------------------------------------+
When ground crew load the plane, each pallet is stamped with a colored cargo label (slot="perishables"). The automated conveyor system reads the label and slides the pallet into the exact matching internal bay (<slot name="perishables">), regardless of the order in which the pallets arrived on the tarmac.
Technical Deep Dive & Specifications
The Named Slot Specification
In the WHATWG DOM Standard, <slot> elements may specify a name attribute. Elements in the host's Light DOM specify which slot they want to project into using the slot attribute:
<!-- Inside Component's Shadow DOM -->
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot> <!-- Default unnamed slot -->
</main>
<footer>
<slot name="footer"></slot>
</footer>
<!-- Consumer Light DOM Usage -->
<app-layout>
<h1 slot="header">Dashboard Analytics</h1>
<p>Main body content automatically goes to the unnamed slot.</p>
<button slot="footer">Confirm & Save</button>
</app-layout>
Distribution Algorithm Matrix & Edge Cases
| Scenario | Light DOM Attribute | Shadow DOM Slot | Result / Behavior |
|---|---|---|---|
| Exact Named Match | <span slot="nav"> |
<slot name="nav"> |
Projected cleanly into <slot name="nav">. |
| No Slot Attribute | <p>Text</p> |
<slot></slot> |
Projected into the default (unnamed) slot. |
| Multiple Elements Same Slot | <li slot="item">A</li><li slot="item">B</li> |
<slot name="item"> |
Both elements are projected into that slot in their Light DOM document order. |
| Unmatched Slot Name | <div slot="sidebar"> |
No <slot name="sidebar"> |
Element is omitted from the Composed Tree (hidden from visual rendering and accessibility tree). |
| Duplicate Shadow Slots | <p slot="info"> |
<slot name="info"> #1 AND <slot name="info"> #2 |
First slot wins. All nodes matching "info" are projected into the first <slot name="info">. The second slot receives nothing. |
LIGHT DOM SHADOW DOM
+-------------------------+ +-------------------------+
(1) | <h2 slot="title"> | ───────────> | <slot name="title"> |
+-------------------------+ +-------------------------+
(2) | <button slot="actions"> | ────┐ +-------------------------+
+-------------------------+ └──────> | <slot name="actions"> |
(3) | <button slot="actions"> | ────┘ +-------------------------+
+-------------------------+ +-------------------------+
(4) | <p>General paragraph</p>| ───────────> | <slot> (Default Catch) |
+-------------------------+ +-------------------------+
(5) | <span slot="missing"> | ───────────> ❌ [Dropped / Not Drawn]
+-------------------------+
Styling Named Slots
You can target elements projected into specific slots using attribute selectors inside ::slotted():
/* Inside Shadow DOM */
::slotted([slot="header"]) {
font-size: 1.5rem;
font-weight: bold;
color: #38bdf8;
}
::slotted([slot="actions"]) {
display: flex;
gap: 0.5rem;
}
💻 Interactive Code Playground
Starter Code
Line-by-Line Code Breakdown
- Line 18–33 (
<dashboard-card>...): Consumer light DOM markup declaring children bound to"icon","title","actions","footer", and the default unnamed slot. - Line 81–87 (
<slot name="icon">,<slot name="title">,<slot name="actions">): Named slot injection points in the header bar. - Line 90 (
<slot></slot>): The default slot rendering the two unannotated<p>tags. - Line 94 (
<slot name="footer"></slot>): Named slot for status metadata and timestamps.
Expected Browser Render Output
+-------------------------------------------------------------------+
| 📊 Real-Time User Traffic [Export CSV] [Refresh]|
+-------------------------------------------------------------------+
| Current active concurrent sessions: 14,892 |
| Edge ingress latency: 18ms (99th percentile). |
+-------------------------------------------------------------------+
| Telemetry updated 3 seconds ago via WebSocket. |
+-------------------------------------------------------------------+🏋️ Hands-On Exercise
🎯 The Challenge: Build a Reusable <modal-dialog> with 3 Named Slots
Instructions:
- Define a
<modal-dialog>custom element with open Shadow DOM. - Provide 3 named slots:
name="header"(Modal title and icon)name="body"(Main interactive body or form)name="footer"(Action buttons: Cancel, Submit)
- Include an overlay backdrop (
position: fixed; inset: 0; background: rgba(0,0,0,0.7); display: flex; align-items: center; justify-content: center;). - Support closing the modal when clicking an internal
✕button that dispatches a custom'close-modal'event.
🏁 Starter Code Sandbox
⚠️ Common Pitfalls
- Misspelled Slot Names: If consumer writes
<h2 slot="heeder">instead ofslot="header", the element will not match any named slot and will be completely hidden from the rendered view. - Placing Multiple
<slot name="xyz">with the Same Name in Shadow DOM: The browser only projects matching nodes into the first slot with that name. Subsequent slots with the same name remain empty. - Attempting to Slot Text Nodes Directly into Named Slots: Text nodes without an enclosing element cannot have attributes (you cannot write
"My Title" slot="header"). Always wrap text in a<span>,<h2>, or<div>to assign aslotattribute.
💡 Pro Tips
- Multiple Elements in a Single Slot: Remember that a named slot can receive multiple Light DOM nodes. For example,
<slot name="actions">can receive 4 distinct<button slot="actions">elements rendered consecutively. - Progressive Slot Forwarding: In complex composite components (e.g.
<table-widget>wrapping<table-row>), you can forward slots through multiple component layers by declaring<slot name="header" slot="header"></slot>.
📌 Key Takeaways
- Named slots use
<slot name="identifier">in the Shadow DOM andslot="identifier"in the Light DOM. - Elements without a
slotattribute default to the unnamed<slot></slot>. - Multiple Light DOM elements can target the same named slot, rendering in source document order.
- Light DOM elements targeting a non-existent slot name are omitted from the rendered Composed Tree.
- Duplicate named slots inside the same shadow root are resolved by assigning all nodes to the first instance.
- --