Slots
Passing template content — not just data — into a component, and how named and scoped slots extend the pattern.
អាន 2 នាទី
Props pass data into a component. Slots pass markup into a component, letting the parent decide what content goes inside a piece of structure the child owns.
A default slot
<!-- CardBox.vue -->
<template>
<div class="card">
<slot></slot>
</div>
</template><!-- App.vue -->
<template>
<CardBox>
<h3>Product name</h3>
<p>Product description.</p>
</CardBox>
</template>Whatever's written between <CardBox> and </CardBox> in the parent gets rendered in place of <slot></slot> inside CardBox. CardBox owns the styling and structural wrapper (.card); the parent decides what actually goes inside it. This is a fundamentally different relationship than props: a prop like title="..." would only let the parent supply a string, not arbitrary markup with its own headings, images, or nested components.
Fallback content
A <slot> can have default content, used whenever the parent doesn't provide any:
<template>
<button class="btn">
<slot>Submit</slot>
</button>
</template><MyButton /> renders "Submit"; <MyButton>Save changes</MyButton> overrides it.
Named slots
A component can expose more than one slot, each with a name, for content that goes in different structural positions:
<!-- LayoutCard.vue -->
<template>
<div class="card">
<header><slot name="header"></slot></header>
<main><slot></slot></main>
<footer><slot name="footer"></slot></footer>
</div>
</template><!-- App.vue -->
<template>
<LayoutCard>
<template #header><h2>Order #1024</h2></template>
<p>2 items, shipped yesterday.</p>
<template #footer><button>Track package</button></template>
</LayoutCard>
</template>#header is shorthand for v-slot:header. Content with no <template #name> wrapper falls into the unnamed default slot — here, the <p>.
Scoped slots: passing data back out to the slot content
Sometimes the child has data that only makes sense in the context of what the parent wants to render — a list component that knows each item, but not how the parent wants to display it. Scoped slots let a component pass data into the slot content it renders:
<!-- ItemList.vue -->
<script setup>
defineProps(["items"]);
</script>
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item"></slot>
</li>
</ul>
</template><!-- App.vue -->
<template>
<ItemList :items="products">
<template #default="{ item }">
<strong>{{ item.name }}</strong> — ${{ item.price }}
</template>
</ItemList>
</template>ItemList handles the looping and structure; the parent decides exactly how each item renders, using data (item) that only ItemList actually has. This is what makes scoped slots powerful for genuinely reusable components — a data table, a dropdown, a list — where the wrapping behavior is shared but the per-item presentation legitimately varies by caller.