Slot
Slots are a foundational content distribution mechanism in component-based architecture. They allow a child component to define placeholder areas within its template, while the parent component can inject custom template fragments into these designated spots. This design pattern ensures that a component maintains a consistent, fixed structure while remaining open to external customization. In modern frameworks like Vue, slots are typically categorized into default slots, named slots, and scoped slots.
The default slot is the simplest and most common form of content injection. A child component uses the <slot> element as a placeholder, and any content placed inside the child component's tags by the parent will be rendered at that location.
<!-- BaseCard.vue -->
<template>
<div class="card">
<h3>Card Title</h3>
<div class="card-body">
<slot>Default fallback content</slot>
</div>
</div>
</template>
The parent component consumes it like this:
<BaseCard>
<p>This content is provided by the parent component.</p>
</BaseCard>
The text "Default fallback content" nested inside the <slot> tag acts as a fallback. It only appears if the parent component fails to provide any custom content for that slot.
Named Slots
When a component requires multiple distinct injection zones, named slots come into play. By assigning a name attribute to the <slot> element, developers can target specific areas using the v-slot directive or its shorthand #.
<!-- PageLayout.vue -->
<template>
<div class="layout">
<header><slot name="header"></slot></header>
<main><slot></main>
<footer><slot name="footer"></slot></footer>
</div>
</template>
Parent component usage:
<PageLayout>
<template #header>
<h1>Custom Page Header</h1>
</template>
<p>This goes into the default unnamed slot.</p>
<template #footer>
<small>Copyright © 2026</small>
</template>
</PageLayout>
The default slot can also be explicitly written as #default. Any content not wrapped in a <template> tag targeting a specific name is automatically funneled into the default slot.
Scoped Slots
Normally, slot content is compiled within the parent component's scope, meaning it cannot directly access internal data belonging to the child component. Scoped slots solve this limitation by enabling the child component to pass data back up to the parent through slot props, giving the parent the flexibility to decide how that data should be rendered.
<!-- UserList.vue -->
<template>
<ul>
<li v-for="(user, index) in users" :key="user.id">
<slot :user="user" :index="index">
{{ user.name }}
</slot>
</li>
</ul>
</template>
<script setup>
defineProps({
users: {
type: Array,
default: () => []
}
})
</script>
In the parent component:
<UserList :users="users">
<template #default="{ user, index }">
<span>{{ index + 1 }}. {{ user.name }} - {{ user.email }}</span>
</template>
</UserList>
Here, the child exposes user and index, which the parent retrieves via object destructuring. Scoped slots are ideal for highly reusable components like lists, data tables, and dropdown menus where external custom rendering is required.
Dynamic Slot Names and Fallbacks
Advanced use cases often benefit from dynamic slot names, which allow developers to bind slot identifiers to reactive variables:
<template v-slot:[slotName]>
<p>Dynamic slot content</p>
</template>
This can also be shortened to #[slotName]. Fallback content, meanwhile, is always defined inline within the <slot> boundaries (e.g., <slot name="header">Fallback Header</slot>) and renders automatically when no parent content is supplied.
Best Practices
- Props vs. Slots: Use props to pass raw data and configuration, and use slots to distribute template structures and layout elements.
- Scope Awareness: Slot content naturally evaluates in the parent scope. Switch to scoped slots specifically when you need access to child-internal data.
- Syntactic Sugar: Embrace the
#shorthand for named slots to keep templates clean and readable. - Keep Logic Lean: Avoid heavy business logic inside slot templates; extract complex sub-views into dedicated child components if necessary.
- Version Standards: Modern ecosystems strongly favor the unified
v-slotsyntax, leaving legacy slot attributes behind. - Native Counterparts: Web Components also feature a native
<slot>element working alongside the Shadow DOM for content distribution, though their underlying APIs differ from framework-specific implementations.
Summary
Slots are the cornerstone of component composition and flexibility. Default slots handle single-point content injection, named slots manage multi-region layouts, and scoped slots bridge the gap by exposing internal child data to the outer scope. Mastering slots empowers developers to build highly adaptable, reusable components while preserving strict and clean structural boundaries.