JAVASCRIPT
Building Flexible Components with Vue 3 Scoped Slots
Learn how to create highly reusable Vue 3 components using scoped slots, allowing parent components to fully customize the rendering of child content with passed-down data.
// ChildComponent.vue
<template>
<div>
<h2>Items List</h2>
<div v-for="item in items" :key="item.id" class="item-card">
<slot :item="item" :index="items.indexOf(item)">
<!-- Fallback content if no slot is provided -->
<p>{{ item.name }} (ID: {{ item.id }})</p>
</slot>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
const items = ref([
{ id: 1, name: 'Apple', price: 1.00 },
{ id: 2, name: 'Banana', price: 0.50 },
{ id: 3, name: 'Orange', price: 0.75 }
]);
</script>
<style scoped>
.item-card {
border: 1px solid #eee;
padding: 10px;
margin-bottom: 5px;
border-radius: 4px;
}
</style>
// ParentComponent.vue
<template>
<div>
<h1>Product Catalog</h1>
<ChildComponent v-slot="slotProps">
<div class="custom-item">
<h3>{{ slotProps.item.name }}</h3>
<p>Price: ${{ slotProps.item.price.toFixed(2) }}</p>
<p>Index: {{ slotProps.index }}</p>
</div>
</ChildComponent>
</div>
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
</script>
<style scoped>
.custom-item {
background-color: #f9f9f9;
padding: 15px;
margin-bottom: 10px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
</style>
How it works: Scoped slots in Vue 3 allow child components to pass data back to their parent's slot content. In this example, `ChildComponent` iterates over an `items` array and passes each `item` object and its `index` to its default slot using `<slot :item="item" :index="items.indexOf(item)">`. The `ParentComponent` then receives this data via `v-slot="slotProps"` (or `{ item, index }` for destructuring) and uses it to render custom content, demonstrating powerful content projection and reusability.