JAVASCRIPT
Enhance Component Reusability with Vue 3 Scoped Slots
Learn to leverage Vue 3's scoped slots to pass data from a child component back to its parent's slot content, enabling highly flexible and reusable components where the parent controls rendering logic based on child data.
// ListWrapper.vue (Child Component)
<template>
<div class="list-container">
<h3>{{ title }}</h3>
<slot :items="items" :highlightItem="highlightItem">
<p v-if="items.length === 0">No items available.</p>
<ul v-else>
<li v-for="item in items" :key="item.id" :class="{ 'highlight': item.id === highlightedId }">
{{ item.name }}
</li>
</ul>
</slot>
<button @click="highlightRandom">Highlight Random Item</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps({
title: String,
items: Array
});
const highlightedId = ref(null);
const highlightItem = (id) => {
highlightedId.value = id;
};
const highlightRandom = () => {
if (props.items.length > 0) {
const randomIndex = Math.floor(Math.random() * props.items.length);
highlightedId.value = props.items[randomIndex].id;
}
};
// Expose to slot via <slot :items="items" ...>
defineExpose({ highlightItem });
</script>
<style scoped>
.list-container {
border: 1px solid #ccc;
padding: 15px;
margin: 15px 0;
}
.highlight {
background-color: yellow;
font-weight: bold;
}
</style>
// App.vue (Parent Component)
<template>
<div>
<h1>App Component</h1>
<ListWrapper :title="'My Items'" :items="dataItems">
<!-- Using scoped slot to customize rendering -->
<template #default="{ items, highlightItem }">
<p v-if="items.length === 0">No customized items here.</p>
<div v-else>
<p>Custom Rendered List:</p>
<div v-for="item in items" :key="item.id" @click="highlightItem(item.id)" style="cursor: pointer; padding: 5px; border-bottom: 1px dotted #eee;">
{{ item.name }} (ID: {{ item.id }})
</div>
</div>
</template>
</ListWrapper>
<ListWrapper :title="'Another List'" :items="moreItems" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import ListWrapper from './ListWrapper.vue';
const dataItems = ref([
{ id: 1, name: 'Item A' },
{ id: 2, name: 'Item B' },
{ id: 3, name: 'Item C' }
]);
const moreItems = ref([
{ id: 10, name: 'Alpha' },
{ id: 20, name: 'Beta' }
]);
</script>
How it works: This snippet demonstrates Vue 3's scoped slots, a powerful feature for creating highly reusable components with customizable rendering. The `ListWrapper` component defines a default slot and binds internal data (`items`, `highlightItem`) to it using `<slot :items="items" :highlightItem="highlightItem">`. The parent `App.vue` can then consume this slot, accessing the provided data via `v-slot="{ items, highlightItem }"` (or `#default="{ ... }"`), to completely control how the child's data is rendered within its own template. This allows the `ListWrapper` to manage its internal logic and data, while the parent dictates the UI presentation, leading to flexible component design.