In NOVA LAB the inventory system provides data about each cargo module, but the operator decides how to display it - as a list, table, or cards. Scoped slots are a mechanism for passing data from a child to a parent through a slot.
Regular slots give the parent control over appearance, but what if the child has data the parent should use? For example, the child iterates over a list and wants to give the parent control over rendering each item.
The child passes data as attributes on the
<slot> tag:1<!-- ModuleList.vue (child) -->
2<script setup>
3import { ref } from 'vue'
4const items = ref([
5 { id: 1, name: 'Oxygen Tank', status: 'loaded' },
6 { id: 2, name: 'Water Filter', status: 'pending' },
7 { id: 3, name: 'Solar Cell', status: 'damaged' }
8])
9</script>
10
11<template>
12 <div class="module-list">
13 <div v-for="item in items" :key="item.id">
14 <slot :item="item" :index="item.id">
15 <!-- Fallback: simple rendering -->
16 <p>{{ item.name }}</p>
17 </slot>
18 </div>
19 </div>
20</template>The parent uses
v-slot or #default with an argument:1<template>
2 <!-- Full syntax -->
3 <ModuleList v-slot="slotProps">
4 <div class="card">
5 <h3>{{ slotProps.item.name }}</h3>
6 <span>{{ slotProps.item.status }}</span>
7 </div>
8 </ModuleList>
9
10 <!-- With destructuring -->
11 <ModuleList v-slot="{ item, index }">
12 <div class="card">
13 <h3>#{{ index }}: {{ item.name }}</h3>
14 <span :class="item.status">{{ item.status }}</span>
15 </div>
16 </ModuleList>
17</template>Scoped slots also work with named slots:
1<!-- DataTable.vue -->
2<template>
3 <table>
4 <thead>
5 <tr>
6 <slot name="header">
7 <th>Name</th>
8 <th>Status</th>
9 </slot>
10 </tr>
11 </thead>
12 <tbody>
13 <tr v-for="item in items" :key="item.id">
14 <slot name="row" :item="item">
15 <td>{{ item.name }}</td>
16 <td>{{ item.status }}</td>
17 </slot>
18 </tr>
19 </tbody>
20 </table>
21</template>
22
23<!-- Parent -->
24<DataTable>
25 <template #header>
26 <th>Module Name</th>
27 <th>Current Status</th>
28 <th>Actions</th>
29 </template>
30
31 <template #row="{ item }">
32 <td>{{ item.name }}</td>
33 <td>{{ item.status }}</td>
34 <td><button>Inspect</button></td>
35 </template>
36</DataTable>