Vue slots let parent components pass template markup directly into child component layouts. While props pass JavaScript data down, slots pass full HTML or other components into designated placeholders.
Slots make wrapper components like cards, modals, dialogs, and data tables deeply reusable. The child controls outer layout and spacing, while the parent controls the inner content.
flowchart TD
Parent["Parent Template Markup"] -->|Fills Placeholder| Slot["Child slot Element"]
Child["Child Component Data"] -->|Scoped Slot Props| Parent
<ModalCard>...</ModalCard> tags.<slot name="header"> using <template #header>.<slot :row="item" />.First, define ModalCard.vue with a named header slot (including fallback markup), a default slot, and a scoped footer slot.
<script setup>
defineProps({
title: String
});
</script>
<template>
<div class="card">
<header class="card-header">
<slot name="header">
<h3>{{ title || 'Default Card Title' }}</h3>
</slot>
</header>
<main class="card-body">
<slot>Default body content appears when nothing is passed.</slot>
</main>
<footer class="card-footer">
<slot name="footer" :timestamp="new Date().getFullYear()" />
</footer>
</div>
</template>
Next, pass custom markup from App.vue using the # shorthand for v-slot: and read the scoped timestamp property.
<script setup>
import ModalCard from './components/ModalCard.vue';
</script>
<template>
<ModalCard>
<template #header>
<h2 class="accent">Confirm Account Update</h2>
</template>
<p>This paragraph fills the default slot area automatically.</p>
<template #footer="{ timestamp }">
<small>Copyright {{ timestamp }}</small>
<button>Confirm</button>
</template>
</ModalCard>
</template>
#header on a Plain <div> Instead of <template>: Named slot directives like #header or v-slot:header must be placed on a <template> tag (or directly on the component tag for a single default slot).<slot /> empty renders nothing if the parent omits content. Add helpful fallback markup inside <slot>Fallback</slot> when appropriate.v-slot?Replace v-slot: with #. For example, v-slot:header becomes #header, and v-slot:default becomes #default.
<script setup> whether a parent passed a slot?Call useSlots() from 'vue' inside <script setup>, or check $slots.footer inside <template> to hide empty wrapper elements conditionally.
Yes. Write <template #[dynamicSlotName]> in the parent template, where dynamicSlotName is a reactive ref holding a string.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With