← Back to all snippets
JAVASCRIPT

Advanced Component Composition with Scoped Slots

Enhance component reusability and flexibility in Vue 3 by mastering scoped slots, allowing parent components to customize child component rendering with access to child data.

// src/components/DataList.vue (Child Component)
<template>
  <div class="data-list">
    <h3>{{ title }}</h3>
    <ul>
      <li v-for="item in items" :key="item.id">
        <!-- Scoped slot: exposing 'item' and 'index' data -->
        <slot :item="item" :index="index">
          {{ item.name }} <!-- Fallback content -->
        </slot>
      </li>
    </ul>
  </div>
</template>

<script setup>
import { defineProps } from 'vue';

defineProps({
  title: String,
  items: {
    type: Array,
    required: true
  }
});
</script>

// src/App.vue (Parent Component)
<template>
  <div id="app">
    <h1>Product List</h1>
    <DataList :items="products" title="Available Products">
      <!-- Using v-slot to access scoped slot props -->
      <template v-slot="{ item, index }">
        <div class="product-item">
          <span>{{ index + 1 }}. <strong>{{ item.name }}</strong> - ${{ item.price }}</span>
          <span v-if="item.inStock" class="status-badge">In Stock</span>
          <span v-else class="status-badge out-of-stock">Out of Stock</span>
        </div>
      </template>
    </DataList>

    <hr />

    <h1>User List (default rendering)</h1>
    <DataList :items="users" title="Registered Users" />
  </div>
</template>

<script setup>
import { ref } from 'vue';
import DataList from './components/DataList.vue';

const products = ref([
  { id: 1, name: 'Laptop', price: 1200, inStock: true },
  { id: 2, name: 'Mouse', price: 25, inStock: false },
  { id: 3, name: 'Keyboard', price: 75, inStock: true },
]);

const users = ref([
  { id: 101, name: 'Alice Smith' },
  { id: 102, name: 'Bob Johnson' },
]);
</script>

// Basic CSS for demonstration (optional)
<style>
.product-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 0;
  border-bottom: 1px solid #eee;
}
.status-badge {
  font-size: 0.8em;
  padding: 4px 8px;
  border-radius: 4px;
  background-color: #e0ffe0;
  color: #007000;
}
.status-badge.out-of-stock {
  background-color: #ffe0e0;
  color: #700000;
}
</style>
How it works: Scoped slots are a powerful Vue 3 feature allowing a child component to pass data back to its parent's slot content, enabling highly flexible and reusable components. In this example, `DataList.vue` iterates over a list and exposes each `item` and its `index` to its default slot. The parent `App.vue` consumes this slot using `<template v-slot="{ item, index }">`, gaining access to the child's data to render completely customized content for each list item, while `DataList` remains responsible for the list structure.

Need help integrating this into your project?

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

Hire DigitalCodeLabs