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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs