Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Vue Dictionary
  3. Scoped Slot

Scoped Slot

Since: Vue 2(2016)

Scoped slots in Vue allow a child component to expose its own data so it can be used inside the slot template defined by the parent. With a regular slot, the template can only access data from the parent scope. Scoped slots let the parent template reference data managed by the child component, giving the parent full control over how that data is rendered.

Syntax

<!-- Child component: pass data to the parent via v-bind on <slot> -->

<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      <!-- :item="item" exposes the item as a slot prop available to the parent -->
      <slot :item="item">{{ item.name }}</slot>
    </li>
  </ul>
</template>
<!-- Parent component: receive slot props using v-slot="variableName" -->

<template>
  <MyList>
    <!-- slotProps contains all props passed by the child -->
    <template v-slot:default="slotProps">
      <strong>{{ slotProps.item.name }}</strong>
    </template>
  </MyList>
</template>
<!-- Shorthand using destructuring assignment -->

<template>
  <MyList>
    <!-- Destructure with { item } to access the prop directly -->
    <template #default="{ item }">
      <strong>{{ item.name }}</strong>
    </template>
  </MyList>
</template>
<!-- Named scoped slot: combine a slot name with v-bind -->

<!-- Child component -->
<template>
  <div>
    <!-- Pass row data to the "item" named slot -->
    <slot name="item" :row="row" v-for="row in rows" :key="row.id"></slot>
  </div>
</template>
<!-- Parent component: receive the slot using v-slot:slotName="variableName" -->

<template>
  <MyTable>
    <!-- Specify the slot name and destructure the prop in one step -->
    <template v-slot:item="{ row }">
      <td>{{ row.name }}</td>
      <td>{{ row.price }}</td>
    </template>
  </MyTable>
</template>

Scoped Slot Syntax Reference

SyntaxDescription
v-slot:default="variableName"Receives all slot props from the default slot as an object. Access individual values with variableName.propName.
v-slot:default="{ propName }"Uses destructuring to extract a slot prop directly. The prop can then be referenced by name without a wrapper object.
#default="{ propName }"Shorthand for v-slot:default. #slotName is equivalent to v-slot:slotName.
v-slot:slotName="variableName"Receives slot props from a named scoped slot. Specifies both the slot name and the variable to bind the props to.
:propName="value" (child side)Passes data from the child's <slot> tag to the parent using v-bind. Multiple props can be passed at once.

Sample Code

This example shows a child component that manages a product list and delegates the rendering of each row to the parent component.

<!-- ProductList.vue (child component: manages product data and delegates rendering to the parent) -->

<template>
  <ul class="product-list">
    <li v-for="product in products" :key="product.id">
      <!-- Expose the entire product object as a slot prop to the parent -->
      <slot :product="product">
        <!-- Fallback content shown when the parent provides no template -->
        {{ product.name }}
      </slot>
    </li>
  </ul>
</template>

<script>
export default {
  name: 'ProductList',
  data() {
    return {
      // List of products to display
      products: [
        { id: 1, name: 'Laptop',   price: 89800, stock: 5 },
        { id: 2, name: 'Mouse',    price: 2980,  stock: 20 },
        { id: 3, name: 'Keyboard', price: 6800,  stock: 0 },
      ],
    };
  },
};
</script>
<!-- App.vue (parent component: receives slot props and renders with a custom layout) -->

<template>
  <div>
    <ProductList>
      <!-- Destructure the slot prop with #default="{ product }" -->
      <template #default="{ product }">

        <!-- Toggle style based on stock availability -->
        <span :class="{ 'out-of-stock': product.stock === 0 }">
          {{ product.name }}
        </span>

        <!-- Display the price aligned to the right -->
        <span class="price">{{ product.price.toLocaleString() }}</span>

        <!-- Show an "Out of stock" badge when stock is 0 -->
        <span v-if="product.stock === 0" class="badge">Out of stock</span>

      </template>
    </ProductList>
  </div>
</template>

<script>
// Import and register ProductList as a component
import ProductList from './ProductList.vue';

export default {
  name: 'App',
  components: { ProductList },
};
</script>

<style scoped>
/* Gray out items that are out of stock */
.out-of-stock { color: #aaa; text-decoration: line-through; }

/* Display the price with a left margin in bold */
.price { margin-left: 8px; font-weight: bold; }

/* Style for the out-of-stock badge */
.badge { margin-left: 6px; padding: 2px 6px; background: #e55; color: #fff; border-radius: 4px; font-size: 0.75em; }
</style>

The next example shows how to pass multiple slot props for finer-grained control.

<!-- DataTable.vue (child component: exposes both the row data and its index) -->

<template>
  <table>
    <tbody>
      <tr v-for="(row, index) in rows" :key="row.id">
        <!-- Expose both row (row data) and index (row number) to the parent -->
        <slot name="row" :row="row" :index="index"></slot>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  name: 'DataTable',
  props: {
    // Accept an array of row data from the parent
    rows: {
      type: Array,
      required: true,
    },
  },
};
</script>
<!-- App.vue (parent component: destructures both row and index from the slot) -->

<template>
  <DataTable :rows="members">
    <!-- Receive the named scoped slot with v-slot:row="{ row, index }" -->
    <template v-slot:row="{ row, index }">
      <!-- Use index to display a 1-based row number -->
      <td>{{ index + 1 }}</td>
      <td>{{ row.name }}</td>
      <td>{{ row.role }}</td>
    </template>
  </DataTable>
</template>

<script>
import DataTable from './DataTable.vue';

export default {
  name: 'App',
  components: { DataTable },
  data() {
    return {
      // List of members to display in the table
      members: [
        { id: 1, name: 'Alice', role: 'Admin' },
        { id: 2, name: 'Bob',   role: 'Editor' },
        { id: 3, name: 'Carol', role: 'Viewer' },
      ],
    };
  },
};
</script>

Overview

Scoped slots in Vue allow a child component to expose its internal data to the slot template provided by the parent. On the child side, data is made available using v-bind on the <slot> tag: <slot :propName="value">. On the parent side, the data is received via v-slot="variableName" or with destructuring using v-slot="{ propName }".

This pattern is especially useful for components with repeating structures such as lists and tables. The child component handles data management and iteration, while the parent decides how each row or item is marked up. This makes it possible to reuse the same child component across multiple pages with different rendering styles.

The shorthand #slotName="variableName" can be used in place of v-slot:slotName. For the default slot, write it as #default="{ propName }". For more on slots in general, see slot. For component basics, see component. For passing data between parent and child, see props.

If you find any errors or copyright issues, please .