Vue Slots
What are Slots?
Slots are Vue's content distribution mechanism — they let a parent component inject HTML content into a child component's template. Think of slots as placeholders that the parent fills in. This makes components highly reusable and flexible.
| Type | Description | Syntax |
|---|---|---|
| Default slot | Single unnamed slot | <slot /> |
| Named slots | Multiple slots with names | <slot name="header" /> |
| Scoped slots | Child passes data back to parent | <slot :item="item" /> |
| Fallback content | Default content if no slot provided | <slot>Default</slot> |
<!-- components/BaseCard.vue -->
<template>
<div class="card">
<!-- Named slot: header -->
<div v-if="$slots.header" class="card-header">
<slot name="header" />
</div>
<!-- Default slot with fallback content -->
<div class="card-body">
<slot>
<p class="text-muted">No content provided.</p>
</slot>
</div>
<!-- Named slot: footer -->
<div v-if="$slots.footer" class="card-footer">
<slot name="footer" />
</div>
</div>
</template>
<!-- components/Modal.vue -->
<template>
<Teleport to="body">
<div v-if="modelValue" class="modal-overlay" @click.self="$emit('update:modelValue', false)">
<div class="modal">
<div class="modal-header">
<slot name="title"><h3>Modal</h3></slot>
<button @click="$emit('update:modelValue', false)">×</button>
</div>
<div class="modal-body">
<slot />
</div>
<div v-if="$slots.actions" class="modal-footer">
<slot name="actions" />
</div>
</div>
</div>
</Teleport>
</template>
<script setup>
defineProps({ modelValue: Boolean })
defineEmits(['update:modelValue'])
</script>
<!-- components/DataTable.vue — scoped slots -->
<template>
<table class="data-table">
<thead>
<tr>
<th v-for="col in columns" :key="col.key">
<!-- Scoped slot for column header -->
<slot :name="`header-${col.key}`" :column="col">
{{ col.label }}
</slot>
</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in data" :key="row.id || rowIndex">
<td v-for="col in columns" :key="col.key">
<!-- Scoped slot: passes row data to parent -->
<slot
:name="`cell-${col.key}`"
:row="row"
:value="row[col.key]"
:index="rowIndex"
>
{{ row[col.key] }}
</slot>
</td>
</tr>
</tbody>
</table>
</template>
<script setup>
defineProps({
columns: { type: Array, required: true },
data: { type: Array, required: true },
})
</script>
<!-- Parent.vue -->
<template>
<div>
<!-- BaseCard with named slots -->
<BaseCard>
<template #header>
<h2>User Profile</h2>
</template>
<!-- Default slot -->
<p>Name: Alice Smith</p>
<p>Email: alice@example.com</p>
<template #footer>
<button @click="edit">Edit</button>
<button @click="delete_">Delete</button>
</template>
</BaseCard>
<!-- Card with no content — shows fallback -->
<BaseCard />
<!-- Modal with named slots -->
<Modal v-model="showModal">
<template #title><h3>Confirm Delete</h3></template>
<p>Are you sure you want to delete this item?</p>
<template #actions>
<button @click="showModal = false">Cancel</button>
<button @click="confirmDelete">Delete</button>
</template>
</Modal>
<!-- DataTable with scoped slots -->
<DataTable :columns="columns" :data="users">
<!-- Custom cell rendering via scoped slot -->
<template #cell-status="{ value }">
<span :class="`badge badge-${value}`">{{ value }}</span>
</template>
<template #cell-actions="{ row }">
<button @click="editUser(row)">Edit</button>
<button @click="deleteUser(row.id)">Delete</button>
</template>
</DataTable>
</div>
</template>
<script setup>
import { ref } from 'vue'
import BaseCard from './BaseCard.vue'
import Modal from './Modal.vue'
import DataTable from './DataTable.vue'
const showModal = ref(false)
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'status', label: 'Status' },
{ key: 'actions', label: 'Actions' },
]
const users = ref([
{ id: 1, name: 'Alice', email: 'alice@example.com', status: 'active' },
{ id: 2, name: 'Bob', email: 'bob@example.com', status: 'inactive' },
])
function edit() { console.log('Edit') }
function delete_() { showModal.value = true }
function confirmDelete() { showModal.value = false; console.log('Deleted') }
function editUser(row) { console.log('Edit user:', row) }
function deleteUser(id) { users.value = users.value.filter(u => u.id !== id) }
</script>
Ready to Level Up Your Skills?
Explore 500+ free tutorials across 20+ languages and frameworks.