Vue 3 Slots

Slots are a little bit similar to Props, but they solve a different problem. They are used when you want to use your own component as a wrapper around dynamic content.

Instead of passing data, like props, from the parent to the child component, you are passing html content. You could of course try this with props, but you would lose access to the dynamic properties that Vue gives you with HTML, which would make it kind of pointless.

Slots allows us to receive HTML content, which may be using Vue features, from outside of the component. The parent component can therefore provide both the HTML, and also the data, using a combination of slots and props.

How to use Slots

The most basic use is as below.

<template>
    <slot></slot>
</template>

You would then use this component like this.

<my_slot_component>
<h1> This is where the content goes</h1>
<p> {{ output some data here }} </p>
</my_slot_component>

So all the content between the custom HTML tags gets passed into the slot you have specified in the component. But this is just the simplest way of using slots.

Using more than one slot

It is often very useful to have multiple slots, but how then do you make them unique, so that Vue knows where to render the correct content? The answer comes in two parts, Named Slots, and Default Slots.

<template>
    <div>
        <header>
            <slot name="header">
                <h2>The Default</h2>
            </slot>
        </header>
            <slot></slot>
</div>
</template>

In the code above, we have both of these elements. You can have as many named slots as you need, and then the one un-named slot is the default slot. The way you would use this template is shown below.

 <template v-slot:header>
      <h3>{{ fullName }}</h3>
      <base-badge :type="role" :caption="role.toUpperCase()"></base-badge>
    </template>
    <template v-slot:default>
    <p>{{ infoText }}</p>
    </template>

You can see that a named slot is called with v-slot:header for instance, then the closing tag is the end of the named slot’s content. You can also set the default slot content with the v-slot directive. It is useful to use the <template> tags as they do not render any content.

Styling Slots

This takes a little care, as you might be sending HTML into a wrapper component that has scoped styling already, such as <header>. The styling is specified in the component, so if you want to style a header for instance, that is being sent from the parent, you must put this styling in the component.

The $slots property

This is another property provided by Vue, and it holds information about the slot data your component receives for it’s different slots. You can access this data like this.

 <header v-if="$slots.header">
            <slot name="header">
                <h2>The Default</h2>
            </slot>
        </header>

In this code above , we are checking if $slots.header exists ( i.e is truthy), and if it is, we render the element. The advantage this gives is that we don’t have empty HTML tags floating around in our code, which is never a good thing.

Scoped Slots

This is an advanced use of slots. It is used when you want to pass data from inside the component where you defined the slot to the component where you pass the mark-up for the slot. So it is going in the reverse direction to the normal Vue data flow, which is from parent to child.

We do this by adding props to the slot tag, like this.

<li v-for="goal in goals" :key="goal">
    <slot :item="goal" another-prop="...some value" ></slot>
</li>

We can now access this prop in the app.vue page, where we pass in the HTML content for the slot. This is useful if , for instance, the data you want access to to resides in the child component. To access it from the parent you would have to somehow pass this data from child to parent, which is difficult to do in view. With props, we use events and listeners to do this. For slots, we used scoped slots. The code below is how we access these values from the parent component.

 <template #default="slotProps">
        <h2>{{ slotProps.item }}</h2>
        <p>{{ slotProps['another-prop']}}</p>
      </template>

The “slotProps” variable can be anything, it is an object into which all the child component data in the slot is merged, we can then access it with dot notation, i.e. slotProps.item, or bracket notation if the property has a dash in it, like this : slotProps[‘another-prop’] . This way we can use dynamic data from the child component and render it with custom HTML with the parent component.

Note you only need the <template> wrapper if your are using multiple slots, so if you only have one default slot, it is not required (this means all your mark-up goes in one slot only).

This is a niche and advanced feature which is not used all the time.

V-slot shorthand

One final note, you can replace this this code: v-slot:header with #header, which is slightly different to the usual Vue shortcut of ‘:’ .