Effortlessly forward slots to child components in Vue 3 applications.
- Easily forward all slots or specific slots to child components
- Simple and declarative syntax
In Vue applications, it's common to need to forward slots from a parent component to a child component. However, the default way of doing this can be verbose and repetitive. Consider the following example:
<template>
<ChildComponent>
<template v-for="(index, name) in $slots" v-slot:[name]="data">
<slot :name="name" v-bind="data"/>
</template>
</ChildComponent>
<AnotherChildComponent>
<template v-for="(index, name) in $slots" v-slot:[name]="data">
<slot :name="name" v-bind="data"/>
</template>
</AnotherChildComponent>
</template>
Verbose and hard to read!
<template>
<ForwardSlots :slots="$slots">
<ChildComponent/>
<AnotherChildComponent/>
</ForwardSlots>
</template>
Simple and clean!
npm install vue-forward-slots
You can import it in the component where you want to use it.
<script>
import {ForwardSlots} from "vue-forward-slots";
...
</script>
A classic example is that of a table component with multiple levels of nested components.
We can easily define and forward slots to nested components using the ForwardSlots
component.
We define the slots in the root component.
<template>
<TableComponent>
<template #name-header>
<p class="font-bold">
Name
</p>
</template>
// We still have access to the slot data like we would normally
<template #status-cell="{ user }">
<StatusBadge :status="user.status"/>
</template>
</TableComponent>
</template>
We forward the slots to the child components.
<template>
<table>
// Notice that we can wrap multiple components in the ForwardSlots component
<ForwardSlots :slots="$slots">
<TableHeadComponent/>
<TableBodyComponent/>
</ForwardSlots>
</table>
</template>
The TableHeadComponent now has access to the slots defined in the root component. If no slot is provided, it will default to the text in the slot.
<template>
<thead>
<tr>
<th>
<slot name="name-header">
Some default text
</slot>
</th>
<th>
<slot name="status-header">
Some default text
</slot>
</th>
</tr>
</thead>
</template>
The TableBodyComponent also has access to the slots defined in the root component. Notice how we also pass the user data.
<template>
<tbody>
<tr v-for="user in users">
<td>
<slot name="name-cell" :user="user">
{{ user.name }}
</slot>
</td>
<td>
<slot name="status-cell" :user="user">
{{ user.status }}
</slot>
</td>
</tr>
</tbody>
</template>
We could even go a step further and forward the slots to the next level of child components.
<template>
<thead>
<tr>
<th v-for="header in headers">
<ForwardSlots :slots="$slots">
<TableHeaderCell :header="header"/>
</ForwardSlots>
</th>
</tr>
</thead>
</template>
In theory, we could keep forwarding slots to as many levels of child components as we need.
<template>
// For a single slot
<ForwardSlots :slots="$slots" only="header">
<MyComponent/>
</ForwardSlots>
// For multiple slots
<ForwardSlots :slots="$slots" :only="['header', 'footer']">
<MyComponent/>
</ForwardSlots>
</template>
<template>
// For a single slot
<ForwardSlots :slots="$slots" except="sidebar">
<MyComponent/>
</ForwardSlots>
// For multiple slots
<ForwardSlots :slots="$slots" :except="['sidebar', 'footer']">
<MyComponent/>
</ForwardSlots>
</template>