Vue Scoped Slot

Scoped component slots are a feature introduced in Vue 2.1.0. They allow you to pass properties from your child components into a scoped slot and access them from the parent. Sort of like reverse property passing.

Basic example

To create scoped component <slot>, you will first need to pass properties into a named or unnamed slot from your child component.

Here is an example of a ChildComponent that contains unnamed and named <slot> elements:

ChildComponent.vue

<template>
  <div>
    <p>This is a child component.</p>
    <div>
      <p>Default Slot</p>
      <slot v-bind:text="defaultSlotText"></slot>
    </div>
    <div>
      <p>Named Slot</p>
      <slot name="namedSlot" v-bind:text="namedSlotText"></slot>
    </div>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      defaultSlotText: "Default Slot Text",
      namedSlotText: "Named Slot Text"
    }
  }
}
</script>

Then, to use those properties inside a parent component’s v-slot content, create a <template> element tied to the child component’s <slot> element.

Add an attribute to the <template> element and set it to the name that you wish to access the scope properties from. This essentially creates a local variable for anything inside that template, allowing you to access it as if it was in the parent’s scope.

For example, "firstScope", properties passed into the <slot> will be accessible as {{firstScope.exampleProperty}} while secondScope" will be accessed as {{secondScope.exampleProperty}}.

Here is an example of a ParentComponent that references the defaultSlotText and namedSlotText:

ParentComponent.vue

<template>
  <div>
    <p>This is a parent component.</p>
    <ChildComponent>
      <template v-slot="defaultSlotProps">
        <p>{{defaultSlotProps.text}}</p>
      </template>
      <template v-slot:namedSlot="namedSlotProps">
        <p>{{namedSlotProps.text}}</p>
      </template>
    </ChildComponent>
  </div>
</template>
 
<script>
import ChildComponent from './ChildComponent.vue';
 
export default {
  components: {
    ChildComponent
  }
}
</script>

You can also use destructuring in v-slot:

<template>
  <div>
    <p>This is a parent component.</p>
    <ChildComponent>
      <template v-slot="{ text }">
        <p>{{ text }}</p>
      </template>
    </ChildComponent>
  </div>
</template>

Viewing the application in a web browser will produce the following result:

<div>
  <p>This is a parent component.</p>
  <div>
    <p>This is a child component.</p>
    <div>
      <p>Default Slot</p>
      <p>Default Slot Text</p>
    </div>
    <div>
      <p>Named Slot</p>
      <p>Named Slot Text</p>
    </div>
  </div>
</div>

Fancy List Example

You may be wondering what would be a good use case for scoped slots. Here’s an example: imagine a <FancyList> component that renders a list of items - it may encapsulate the logic for loading remote data, using the data to display a list, or even advanced features like pagination or infinite scrolling. However, we want it to be flexible with how each item looks and leave the styling of each item to the parent component consuming it. So the desired usage may look like this:

<FancyList :api-url="url" :per-page="10">
  <template #item="{ body, username, likes }">
    <div class="item">
      <p>{{ body }}</p>
      <p>by {{ username }} | {{ likes }} likes</p>
    </div>
  </template>
</FancyList>

Inside <FancyList>, we can render the same <slot> multiple times with different item data (notice we are using v-bind to pass an object as slot props):

<ul>
  <li v-for="item in items">
    <slot name="item" v-bind="item"></slot>
  </li>
</ul>

Reference

Slots | Vue.js How To Use Scoped Component Slots in Vue.js | DigitalOcean