Vue.js: notes on Slots

Vue slots allow us to create generic components, by passing template content (custom markup) to child components.

Let's say we have a MyButton component which we'd like to make generic. We can set a slot inside its template:

<button>
  <slot></slot>
</button>

We can then pass custom content to MyButton:

<MyButton>Submit</MyButton>

The HTML node Submit replaces the tag <slot></slot> when the component gets rendered.

Optionally, we can specify some fallback content, to be rendered when the parent does not provide content for a slot:

<button>
  <slot>Click me</slot>
</button>

We're not limited to text, we can also pass any valid template content:

<MyButton>
  <strong>S</strong>ubmit
</MyButton>

Since we didn't explicitly name the slot above, it will implicitly become the default slot, named default. The following achieves the same result as the above:

<MyButton>
  <template v-slot:default>
    <strong>S</strong>ubmit
  </template>
</MyButton>

Instead of v-slot:, we can use the shorthand #, as in <template #default>.

Named slots start to become useful when we want our component to provide more than one slot:

<button>
  <slot name="emoji"></slot>
  <slot>Click me</slot>
</button>
<MyButton>
  <template v-slot:emoji>๐Ÿ‘</template>
  <template v-slot:default>
    <strong>S</strong>ubmit
  </template>
</MyButton>

Sometimes we may have some state inside our child component that we'd need the parent to be aware of (to render the content of a slot). We can use scoped slots to achieve that:

<button @mouseenter="hover = true" @mouseleave="hover = false">
  <slot name="emoji" :hover="hover"></slot> <slot>Submit</slot>
</button>

<MyButton>
  <template #emoji="{ hover }">{{ hover ? '๐Ÿ‘' : '๐Ÿ‘Ž' }}</template>
</MyButton>