What is a Slot?
In HTML, the <slot> element (part of Web Components) acts as a placeholder with in a custom element. This placeholder is later filled with custom markup.
Example (Web Components):
<template id="element-details-template">
<slot name="element-name">Fallback Text</slot>
</template>
<element-details>
<span slot="element-name">Item A</span>
</element-details>
<element-details>
<span slot="element-name">Item B</span>
</element-details>
To render this, register the custom element:
customElements.define('element-details', class extends HTMLElement {
constructor() {
super();
const template = document.getElementById('element-details-template').content;
this.attachShadow({ mode: 'open' }).appendChild(template.cloneNode(true));
}
});
In Vue, a slot reserves a position in a component’s template. When the component is used, content inside its tag replaces the slot. Think of it like a game console (slot) for game cartridges (custom content).
Use Cases
Slots enable component extension and customization. Instead of rewriting a reusable component for minor changes, slots let you inject content into specific positions. Use cases include layout components, table columns, dropdowns, and modals.
Slot Classification
Vue slots are categorized into three types:
Default Slot
The child component uses <slot> to define a content placeholder. If the parent provides no content, the slot’s fallback is used.
Child Component (Child.vue):
<template>
<slot>
<p>Fallback Content</p>
</slot>
</template>
Parent Component:
<Child>
<div>Default Slot Content</div>
</Child>
Named Slots
Child components use the name attribute to identify multiple slots. The parent targets slots with v-slot:name.
Child Component (Child.vue):
<template>
<slot>Default Fallback</slot>
<slot name="content">Content Fallback</slot>
</template>
Parent Component:
<Child>
<template v-slot:default>Default Named Slot</template>
<template v-slot:content>Custom Content</template>
</Child>
Scoped Slots
Child components bind data to the slot (via props) so the parent can acces it. The parent uses v-slot (or #) to receive these props.
Child Component (Child.vue):
<template>
<slot name="footer" slotData="Child Data">
<h3>No Footer Slot</h3>
</slot>
</template>
Parent Component:
<Child>
<template v-slot:default="slotProps">
Data from Child: {{ slotProps.slotData }}
</template>
<template #default="slotProps">
Data from Child: {{ slotProps.slotData }}
</template>
</Child>
Key Notes
v-slotis only valid on<template>, except for default slots (usable on the component tag).- The default slot is named
default(can be omitted:v-slotinstead ofv-slot:default). - Shorthand
#requires a slot name (e.g.,#default). - Destructuring, ranaming, and defaults:
v-slot="{ slotData: customName, slotData = 'default' }".
Underlying Mechanism
Slots are functions that return VNodes. Vue’s rendering flow is template → render function → VNode → DOM.
Example: Button Counter Component
Component Definition:
Vue.component('button-counter', {
template: '<div><slot>Default Content</slot></div>'
});
Parent Usage:
new Vue({
el: '#app',
template: '<button-counter><span>Slot Content</span></button-counter>',
components: { buttonCounter }
});
The render function for button-counter becomes:
(function anonymous() {
with(this) { return _c('div', [_t("default", [_v("Default Content")])], 2); }
})
Slot Resolution Process
resolveSlots: Groups child nodes by slot name (ordefault).normalizeScopedSlots: Createsvm.$scopedSlotsfor scoped data.
In resolveSlots, nodes with slot attributes are grouped:
function resolveSlots(children, context) {
// Logic to group nodes by slot name...
return slots;
}
Scoped slots pass data via props in renderSlot, enabling parent access to child data.