w3resource
Vue Tutorial

Slots

Just like in the other tutorials in the advanced series we have looked at thus far, the tutorial on Component Basics is a perquisite.

When Vue 2.6.0 was released, Vue introduced a new unified syntax which is (the v-slot directive) for named and scoped slots. It replaced the slot and slot-scope attributes, which has been deprecated, though not removed, they are still document.

Slot Content

Vue implements a content distribution API which is inspired by the Web Components spec draft, with the use of the element to serve as distribution outlets for content.

This allows us to compose components as shown below:

<navigation-link url="/profile">
  Your Personal Profile
</navigation-link>

And in the template for <navigation-link>, we might have:

<a
  v-bind:href="url"
  class="nav-link"
>
  <slot></slot>
</a>

When the component above renders, the will be replaced by "Your Personal Profile". A slot code can be just about any template code, including HTML:

<navigation-link url="/profile">
  <!-- Add a Font Awesome icon -->
  <span class="fa fa-user"></span>
  Your Profile
</navigation-link>
Or it could even be another component.

Compilation Scope

When we want to use data inside a slot, as in the case below:

<navigation-link url="/profile">
  Logged in as {{ user.name }}
</navigation-link>

The slot must access the same instance properties (same scope) as the rest of the template. It doesn't have access to <mnavigation-link>'s scope. For instance, attempt to access url will not work:

<navigation-link url="/profile">
  Clicking here will redirect you to: {{ url }}
  <!--
The `url` will return undefined, because the content is passed
  _to_ <navigation-link>, instead of being defined _inside_ the
  <navigation-link< component.
  -->
</navigation-link>

Fallback Content

There are cases when specifying fallback content for a slot is useful, this will be rendered only when no content is provided. Take for example a <submit-button> component:

<button type="submit">
  <slot></slot>
</button>

To add "submit" as a fallback content, which will be rendered inside the button most times. We have to place it in between the required <slot> tags:

<button type="submit">
  <slot>Submit</slot>
</button>

Hence, writing <submit-button></submit-button> will render "submit". However, when we write <submit-button>save</submit-button> it will render the provided content ("save") instead.

Named Slots

Sometimes it is useful to have multiple slots. As in a <base-layout> component with this template:

<div class="container">
  <header>
    <!-- header content goes here -->
  </header>
  <main>
    <!--Main content goes here -->
  </main>
  <footer>
    <!-- footer content lives here -->
  </footer>
</div>

In cases as these, additional slots can be defined using the <slot> special attribute, name:

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

Any <slot> outlet that doesn't have a name attribute, implicitly has the name "default"

Scoped Slots

There are times when it is useful for the slot content to access data that is only available in the child component. For instance, given a <current-user> component which has the following template:

<span>
  <slot>{{ user.lastName }}</slot>
</span>

If we want to make user available to the parent's slot cotent, we have to bind user as an attribute to the <slot> element:

<span>
  <slot v-bind:user="user">
    {{ user.lastName }}
  </slot>
</span>

When we bind attributes to a <slot> element they are called slot props. Thus, we can use v-slot with a value to define a name for the slot props we have been provided, in the parent scope:

<current-user>
  <template v-slot:default="slotProps">
    {{ slotProps.user.firstName }}
  </template>
</current-user>

Abbreviated Syntax for Lone Default Slots

A component's tag can be used as a slot's template when only default slot is provided content. This will allow us to use v-slot directly on the component:

<current-user v-slot:default="slotProps">
  {{ slotProps.user.firstName }}
</current-user>

However, using the v-slot without an argument will refer to the default slot:

<current-user v-slot="slotProps">
  {{ slotProps.user.firstName }}
</current-user>

Note however, the abbreviated slot syntax cannot be mixed with named slots, this wil lead to

scope ambiguity. Whenever multiple slots exist, use the full &template>based syntax for all the slots.

Destructuring Slot Props

Scoped slots work internally by wrapping our slot content in a function that is passed a single argument:

function (slotProps) {
  // ... slot content ...
}

This essentially means that the value of v-slot can accept just about any JavaScript expression.

Dynamic Slot Names

As from Version 2.6.0, dynamic directive argument equally works on v-slot, thus allowing the definition of dynamic slot names:

<base-layout>
  <template v-slot:[dynamicSlotName]>
    ...
  </template>
</base-layout>

Named Slots Shorthand

This also came with version 2.6.0. Just like v-bind and v-on, the v-slot equally has a shorthand, it replaces everything before a (v-slot: ) argument with the symbol #. For instance, v-slot:header can be written as #header instead.

But this shorthand is available only when an argument is provided. Thus the following syntax is not valid:

<current-user #="{ user }">
  {{ user.firstName }}
</current-user>

But the syntax below is valid:

<current-user #default="{ user }">
  {{ user.firstName }}
</current-user>

Because you specified a name of the slot when using the shorthand.

Other Examples

slot props help us to turn slots into reusable templates that can render different content based on the input props.

This is useful when implementing a <todo-list> component which contains the layout and the logic for filtering a list. In such cases rather than hard-coding the content for each of the todo, we could let the parent component have control by making all the todos a slot, and then binding todo as a slot prop.

Named Slots with the slot Attribute

This feature was deprecated in 2.6.0. we use the special slot attribute on <template> to pass content to named slots from the parents.

Or, you can use the slot attribute on a normal element directly.

Scoped Slots with the slot-scope Attribute

The parent component can use <template> with the slot-scope attribute (using the <slot-example>) to receive props passed to a slot. Any JavaScript expression that can appear in the argument position of a function definition, can be accepted by the value of slot-scope.