6 minutes read

Vue.js is a progressive JavaScript framework that is widely used for building user interfaces. One of the most important features of Vue.js is its component system. This system allows developers to create reusable and encapsulated pieces of code that can be used throughout an application. A key element of this component system is slots, which provide a way to create "placeholders" in a component's template. The clots then can be filled with any content you want when you use that component. In this topic, you'll learn about different types of slots and how to use them for content distribution.

Content distribution with slots

Slots in Vue.js are a mechanism for injecting content into a component's template. They are placeholders inside a component that you can fill with your own custom content, allowing you to compose new components using the ones you've already created. This makes your components more reusable and clean.

For example, consider a simple Card component. This component has a predefined structure consisting of a title, a body, and a footer. However, you may want to customize the content within these sections for each card. By using slots, you can designate these sections as placeholders and fill them with your own content when using the Card component.

<template>
  <div class="card">
    <header>
      <slot name="header"></slot>
    </header>
    <main>
      <slot></slot>
    </main>
    <footer>
      <slot name="footer"></slot>
    </footer>
  </div>
</template>

In the code snippet above, we define three slots inside the Card component: header, the default slot (for the main content), and footer. These slots act as named placeholders. When you use the Card component, you can provide specific content for each of these slots using the v-slot directive. Let's learn more about the slots, especially the default and named slots, in the next section.

You can also put a fallback content in case there is no content provided:

<button type="submit">
  <slot>
    Submit <!-- fallback content -->
  </slot>
</button>

Default and named slots

Vue.js provides two types of slots: default and named slots. The default slot is where any content will go if it's not assigned to a named slot. In the previous example, the main section is a default slot.

Named slots, on the other hand, can be used when you want to distribute content in specific locations of the component's template. These slots have a unique name and can be used to customize different parts of the component. In the Card component example, header and footer are named slots.

When using this component, you can specify which slot to fill using the v-slot directive. In the code example below, we use the v-slot directive to specify which named slot to fill with content. As you can see, the header slot contains a custom title, and the footer slot holds a button. This flexibility allows you to tailor different parts of the Card component to your specific needs.

<Card>
  <template v-slot:header>
    <h1>This is a title</h1>
  </template>
  <p>This is some main content for the card.</p>
  <template v-slot:footer>
    <button>Click me</button>
  </template>
</Card>

In this example, the Card has two slots: header, and footer. When you use Card, you can pass in different content for each slot using the v-slot directive.

In Vue 3, the v-slot directive is still available, but you can also use the # shorthand: <template #header/>

Scoped slots

Scoped slots are a more advanced feature of slots that allow parent components to access data in the child component. This is useful when you want to inject content into the child component, but the content needs access to the child's internal data.

For example, consider a List component that iterates over an array of items. With a scoped slot, you can customize how each item in the list is displayed, and this custom content can access the data of each item.

<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      <slot name="item" :item="item"></slot>
    </li>
  </ul>
</template>

This example uses v-for directive to iterate over a list of items and display each item in a list (<ul>). For each item in the items array, a list item <li> is created. The template also uses a named slot slot name="item". In this case, the slot is named "item".

The :item="item" syntax is passing the current item in the loop as a slot prop to the slot. Slot props allow you to pass data to slots just like you can with component props.

A slot prop in Vue.js is a way to pass data from a child component to a parent component through a slot. When you define a slot in a child component, you can specify the props that it should receive. These props can then be used in the parent component's slot content. This is useful when you want the parent component to have access to data from the child component.

Compile scope

When Vue compiles the templates, it determines the scope based on where the slot is. Content in slots is compiled in the parent scope, while the slot itself is compiled in the child scope. This means that a slot has access to the data from the child component, and the content inside the slot has access to the parent's data.

For example, consider a User component with a slot. If you use this User component and its slot within a parent App component, the User component's data, such as user.name, will indeed be accessible within the slot. However, it's important to clarify that the App component's data is not directly accessible within the slot's content, but you can pass the App component's data into the slot as a prop.

<!-- Child Component (User.vue) -->
<template>
  <div>
      <slot :user="user">{{ user.name }}</slot>
  </div>
</template>

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

const user = ref({ name: 'John Doe', age: 30 });
</script>

In this User component, we're providing a slot and passing the user object to it. If no content is provided by the parent component, the user's name will be displayed by default.

Now, let's see how we can use this component in a parent component:

<!-- Parent Component (App.vue) -->
<template>
  <User>
    <template #default="slotProps">
      <h1>Hello, {{ slotProps.user.name }}!</h1>
      <p>From: {{ appData.location }}</p>
    </template>
  </User>
</template>

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

const appData = ref({ location: 'New York' });
</script>

In the parent component, we're using the User component and providing content for its slot. We're accessing the user object from the slot's scope using the slotProps object (this is the data passed from the child component). We're also accessing the appData object from the App component's own scope (this is the parent's data).

Conclusion

In Vue.js, slots allow for flexible content distribution, making your components more reusable and maintainable. With the use of default, named, and scoped slots, you can create complex and customizable component structures. Understanding how slots work and how to use them effectively is crucial for mastering Vue.js.

How did you like the theory?
Report a typo