A bird.

Max Levytskyi

Frontend Developer

Unlocking the Power of Vue.js 3 Slots

Let’s explore what slots are in Vue.js, how they enhance component reusability, and the best practices for using them effectively.

Unlocking the Power of Vue.js 3 Slots

In the world of Vue.js, props have long been the primary solution for reusing component logic and passing data from parent to child components. They allow developers to customize components and make them dynamic. However, as applications grow in complexity, props can sometimes fall short in providing the necessary flexibility. This is where slots come into play, offering more than just a way to insert templates inside components. Slots provide a powerful mechanism for composing components in a flexible and reusable manner.

Reusability challenges

Passing props is a traditional way to make components reusable. However, sometimes we may want to pass not just a string or an object but an entire template for rendering. In such cases, props alone aren’t sufficient, and that’s where slots come into play.

Let’s look at an example:

App.vue
<template>
  <AwesomeButton>
    Click <b>me</b>!
  </AwesomeButton>
</template>

As you can see, we’re using an AwesomeButton component and want to pass not just text but a more complex template with HTML tags.

Now, let’s take a look at the code for the AwesomeButton component:

AwesomeButton.vue
<template>
  <button>
    <slot />
  </button>
</template>

As you can see, we used only the template and a special element <slot/>, which instructs Vue to insert the passed template into the component at this location.

You can also specify a default value that will be used if the component doesn’t receive a slot.

AwesomeButton.vue
<template>
  <button>
    <slot>Default Button Text</slot>
  </button>
</template>

Try this code in the Vue Playground

Named and multiple slots

Slots aren’t limited to a single, unnamed placeholder. Vue.js allows you to define multiple slots, each with a unique name, enabling more intricate component structures.

Let’s create a card component with a header and main content.

CardComponent.vue
<template>
  <div>
    <div>
      <slot name="header" />
    </div>
    <div>
      <slot />
    </div>
  </div>
</template>

As you can see, we’re using different slots in various locations, so a single slot wouldn’t suffice here. This time, we’ve used a named slot called header and a default slot.

default - is a special name for the slot without a specified name.

Let’s use our new component.

App.vue
<template>
  <CardComponent>
    <template #header>
      <h1>Card Title</h1>
    </template>
    <p>Card Content</p>
  </CardComponent>
</template>

We used the special # syntax to indicate to the compiler that this part of the template is a named slot. In the second slot, we didn’t specify a name, so Vue automatically uses the name default.

Try this code in the Vue Playground

Dynamic slots

Sometimes, we may want to define the slot name dynamically at runtime. For example, in a complex component, content may need to be placed in different locations. This approach makes components even more flexible and reusable.

Let’s take a look at this code:

Vue
<template>
  <div>
    <LargeComponent>
      <template #[position]>
        <b>Dynamic content</b>
      </template>
    </LargeComponent>
    <button @click="position = 'top'">
      Move up
    </button>
    <button @click="position = 'bottom'">
      Move down
    </button>  
  </div>
</template>

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

const position = ref('top');
</script>

As you might guess, here we’ve used a LargeComponent where the content can be displayed either at the top or bottom. When a specific button is clicked, the slot name changes, and the position of the content updates accordingly.

Let’s take a look at the component LargeComponent.

LargeComponent.vue
<template>
  <div>
    <div v-if="$slots.top">
      <slot name="top" />
    </div>
    <div>Here's some content.</div>
    <div v-if="$slots.bottom">
      <slot name="bottom" />
    </div>
  </div>
</template>

Try this code in the Vue Playground

As you can see, we used the $slots property in our template to check for the presence of a slot and display it in a specific section of our component.

In real-world applications, the use cases are countless, allowing you to utilize conditional and named slots in various ways to suit your needs.

Scoped slots: passing data back to parents

Up until now, we’ve used slots without accessing the state of our child components. In fact, we can’t directly use the properties of child components within our slots. However, Vue provides a way to overcome this limitation by allowing us to share specific properties with slots, almost like passing props.

Now, let’s look at this code.

ChildComponent.vue
<template>
  <button @click="counter +=1">
    <slot :counter="counter" />
  </button>
</template>

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

const counter = ref(1);
</script>

Here, we specified that the counter property should be passed to our slot. Now, let’s see how we can use it in the parent component.

App.vue
<template>
  <ChildComponent v-slot="{ counter }">
    Value: {{ counter }}
  </ChildComponent>
</template>

Try this code in the Vue Playground

As you can see, we used v-slot to access the data passed from our child component.

The same approach can be applied to named slots as well.

Summary

Slots are a powerful tool for component composition and reusability, but they’re just one piece of the puzzle. Vue.js 3’s Composition API offers even greater flexibility by allowing you to organize and reuse reactive state and logic in a component-centric manner. Combining slots with the Composition API can lead to highly maintainable and scalable applications. Understanding when and how to use each tool is key to becoming a proficient Vue.js developer.

A bird.

Thanks for reading! Let's stay in touch.

— Max Levytskyi
Frontend Developer
Follow @immaxdev