Wat is een slot?
Stel je maakt een Card component met een mooie rand en schaduw. De inhoud verschilt elke keer — soms een titel met tekst, soms een afbeelding, soms een knop. Wat doe je?
Met slots laat het Card-component een gat waar de parent inhoud in kan stoppen.
Vergelijking met props:
- Props → data doorgeven (strings, numbers, arrays)
- Slots → HTML / componenten doorgeven
Vue vs React: Dit is Vue's tegenhanger van React's children prop, alleen krachtiger — je kunt meerdere gaten maken op verschillende plekken (named slots).
Default slot — de basis
Het child component
<!-- src/components/Card.vue -->
<template>
<div class="card">
<slot />
</div>
</template>
<style scoped>
.card {
border: 1px solid #ccc;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
</style>
Het parent component
<script setup>
import Card from './components/Card.vue'
</script>
<template>
<Card>
<h2>Hallo!</h2>
<p>Dit komt in het slot terecht.</p>
</Card>
<Card>
<img src="/photo.jpg" />
</Card>
</template>
Wat gebeurt er? Alles wat je tussen <Card>...</Card> zet, vervangt de <slot /> in het Card component. De rand, padding en schaduw zijn telkens hetzelfde — de inhoud verschilt.
Fallback content (standaard inhoud)
Wil je iets tonen als de parent niets meegeeft? Zet de fallback binnen de slot-tag.
<!-- Button.vue -->
<template>
<button>
<slot>Klik hier</slot>
</button>
</template>
<!-- Gebruik -->
<Button /> <!-- Toont: Klik hier -->
<Button>Versturen</Button> <!-- Toont: Versturen -->
Named slots — meerdere gaten
Wil je een component met meerdere slots (bijv. header + body + footer)? Geef ze een naam.
Het child component
<!-- src/components/Card.vue -->
<template>
<div class="card">
<header>
<slot name="header" />
</header>
<main>
<slot /> <!-- default slot -->
</main>
<footer>
<slot name="footer" />
</footer>
</div>
</template>
Het parent component
Gebruik <template #naam> om aan te geven welke slot je vult:
<Card>
<template #header>
<h2>Mijn Titel</h2>
</template>
<!-- Zonder # wrapper gaat het naar de default slot -->
<p>Dit is de hoofdtekst.</p>
<template #footer>
<button>Sluiten</button>
</template>
</Card>
De # is een afkorting voor v-slot:. Beide werken:
<template #header>...</template> <!-- kort -->
<template v-slot:header>...</template> <!-- lang -->
Compleet voorbeeld — Modal
Een modal-component is de perfecte plek voor named slots: header, content en footer-knoppen zijn altijd anders, maar de overlay en sluit-knop blijven hetzelfde.
Modal.vue
<!-- src/components/Modal.vue -->
<script setup>
defineProps({
isOpen: Boolean
})
defineEmits(['close'])
</script>
<template>
<div v-if="isOpen" class="overlay" @click="$emit('close')">
<div class="modal" @click.stop>
<header class="modal-header">
<slot name="title">Standaard titel</slot>
<button @click="$emit('close')">×</button>
</header>
<main class="modal-body">
<slot />
</main>
<footer class="modal-footer">
<slot name="actions">
<button @click="$emit('close')">OK</button>
</slot>
</footer>
</div>
</div>
</template>
Gebruik
<script setup>
import { ref } from 'vue'
import Modal from './components/Modal.vue'
const showModal = ref(false)
</script>
<template>
<button @click="showModal = true">Open modal</button>
<Modal :isOpen="showModal" @close="showModal = false">
<template #title>
<h2>Account verwijderen?</h2>
</template>
<p>Dit kun je niet ongedaan maken.</p>
<template #actions>
<button @click="showModal = false">Annuleer</button>
<button @click="deleteAccount">Verwijder</button>
</template>
</Modal>
</template>
Voordeel: Het Modal-component beheert de overlay, focus en sluit-logica één keer. Voor elk soort modal — bevestiging, formulier, foto-viewer — gebruik je dezelfde Modal met andere slot-inhoud.
Belangrijke Regels
- Default slot →
<slot />in child, content tussen tags in parent - Named slot →
<slot name="x" />in child,<template #x>...</template>in parent - Fallback content → tussen de slot-tags:
<slot>default</slot> - Slots gaan over HTML/componenten, props over data
Veelgemaakte Fouten
Fout — slot vergeten in child:
<!-- Card.vue -->
<div class="card">
<!-- ❌ geen slot, parent content verdwijnt -->
</div>
Goed:
<div class="card">
<slot /> <!-- ✅ -->
</div>
Fout — name="..." bij parent in plaats van #:
<Card>
<template name="header">...</template> <!-- ❌ werkt niet -->
</Card>
Goed:
<Card>
<template #header>...</template> <!-- ✅ -->
</Card>