Typing slots with defineSlots

How to easily type your slots thanks to the new defineSlots macro in Vue 3.3>


One of the most abstract concepts in Vue.j is scoped slots.

When you add the complexities of a typing system like TypeScript, the difficulty can increase.

However, with the macro defineSlots introduced in Vue 3.3+, adding types to your slots is easier than ever. Let me show you how.

Using defineSlots

The purpose of the macro is to improve Developer Experience (DX) by adding suggestions and type validation directly in your code editor/IDE. defineSlots accepts a literal type as its parameter, where each property represents the name of a slot, and the value is a function defining the props the slot accepts.

For instance, let's say your actions slot provides a function called open:

TS
defineSlots<{
  actions(props: { open: () => void }): any; // Return type is currently ignored by the macro
}>();

    

Additionally, and as a bonus, defineSlots returns the slots object you would normally get by calling the useSlots macro.

Practical Example with defineSlots

Suppose in your AppProducts component you want to provide product information and define their types.

TS
interface Product {
    id: number;
    name: string;
    price: number;
}

const slots = defineSlots<{
    default(props: {
        highlight: { // You can use literal types
            name: string;
            price: number;
        };
    }): void;
    list(props: {
        products: Product[];
    }): void;
}>();

    

As you can see, we are declaring the types for two slots:

  1. In the default slot, we expect a highlight product with name and price properties.
  2. For the list slot, we send a list of Product.

This straightforward approach has a profound impact. On the one hand, we are properly validating the slot types in the AppProducts component itself. On the other hand, TypeScript will infer the slot types in the “parent” component that uses these slots.

Vue
<template>
    <AppProduct>
      <template #default="{ highlight }">
        <h2>{{ highlight.name }} - ${{ highlight.price }}</h2>
      </template>

      <template #list="{ products }">
        <ul>
          <li v-for="product in products" :key="product.id">
            {{ product.name }} - ${{ product.price }}
          </li>
        </ul>
      </template>
    </AppProduct>
</template>

    

Demo

As always, here’s a demo.

Juan Andrés Núñez
Juan Andrés Núñez
Senior Frontend Engineer. Vue.js specialist. Speaker. Professional teacher. Stoic.