Tutorial

How To Use Component Slots in Vue.js

Vue.js

Introduction

Oftentimes you will need to allow your parent Vue components to embed arbitrary content inside of child components. Vue provides a way to accomplish this with slots.

Note: If you are coming from an Angular background, this is a similar concept to transclusion or content projection.

In this tutorial, you will explore an example Vue project with a parent component and child component that shares content with slots.

Prerequisites

If you would like to follow along with this article, you will need:

This tutorial was verified with Node v15.10.0, npm v7.6.0, and vue v2.6.11.

Using Slots

To allow a parent component to pass DOM elements into a child component, provide a <slot> element inside the child component.

Here is an example of a ChildComponent that contains a <slot>:

ChildComponent.vue
<template>
  <div>
    <p>This is the child component.</p>
    <slot></slot>
  </div>
</template>

Here is an example of a ParentComponent that populates the ChildComponent with content:

ParentComponent.vue
<template>
  <div>
    <p>This is the parent component.</p>
    <ChildComponent>
      <p>This is injected content from the parent component.</p>
      <p>It can still bind to data in the parent's scope: {{myVariable}}</p>
    </ChildComponent>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      myVariable: `Parent Variable`
    }
  }
}
</script>

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

Output
<div> <p>This is the parent component.</p> <div> <p>This is the child component.</p> <p>This is injected content from the parent component.</p> <p>It can still bind to data in the parent's scope: Parent Variable</p> <div> </div>

Content and data from the parent component are injected into the child component.

Providing Fallback Content

If the parent component does not inject any content into the child component’s <slot>, the child component will render any elements inside its <slot> tag:

Here is an example of a ChildComponent with fallback content:

ChildComponent.vue
<template>
  <div>
    <p>This is the child component.</p>
    <slot>
      <p>Fallback Content</p>
    </slot>
  </div>
</template>

Here is an example of a ParentComponent that has two ChildComponents - one with slot content and one without:

ParentComponent.vue
<template>
  <div>
    <p>This is the parent component with slot data.</p>
    <ChildComponent>
      <p>Slot Content</p>
    </ChildComponent>
    <p>This is the parent component without slot data.</p>
    <ChildCmponent></ChildComponent>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  }
}
</script>

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

Output
<div> <p>This is the parent component with slot data.</p> <div> <p>This is the child component.</p> <p>Slot Content</p> <div> <p>This is the parent component without slot data.</p> <div> <p>This is the child component.</p> <p>Fallback Content</p> <div> </div>

Fallback content appears when slot content is not provided by the parent component.

Note: If there is no <slot> element in the child component <template>, any content from the parent component will be silently discarded.

This completes a brief introduction into using <slot> and fallbacks.

Using Named Slots

Having one <slot> element to inject content can satisfy some use cases. However, you may have other use cases where you want to utilize multiple <slot> elements. It is possible to achieve this with Vue with named slots.

Named slots are <slot> elements with a name attribute to allow for namespaced content injection:

<slot name="slotName"></slot>

Consider an example component that features named slots for main and aside:

ChildComponent.vue
<template>
  <div>
    <main>
      <slot name="main"></slot>
    </main>
    <aside>
      <slot name="aside"></slot>
    </aside>
    <slot></slot>
  </div>
</template>

The parent component populates the main and aside slots. Content that does not reference a named slot populates the empty slot:

ParentComponent.vue
<template>
  <div>
    <ChildComponent>
      <template v-slot:main>
        <p>Custom Main</p>
      </template>
      <template v-slot:aside>
        <p>Custom Aside</p>
      </template>
      <div>
        <p>Custom Content</p>
      </div>
    </ChildComponent>
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  }
}
</script>

Note: Prior to Vue 2.6.0, you would have used slot attributes. It has since been replaced with the v-slot directive. Vue 3 will deprecate and eventually remove slot attributes.

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

Output
<div> <div> <main> <p>Custom Main</p> </main> <aside> <p>Custom Aside</p> </aside> <p>Custom Errata</p> <div> </div>

This completes a brief introduction into using named slots.

Conclusion

In this tutorial, you explored a Vue project with a parent component and child component example that shares content with slots.

If you’d like to learn more about Vue.js, check out our Vue.js topic page for exercises and programming projects.

Creative Commons License