Slots
Flexible Komponenten-Layouts durch Content-Projektion — Vues mächtigstes Kompositions-Werkzeug.
Was sind Slots?
Slots ermöglichen es, beliebigen Inhalt in eine Komponente hineinzuprojizieren. Statt nur Daten per Props zu übergeben, kannst du ganzes HTML oder andere Komponenten einbetten. Das macht Slots zum Werkzeug für wiederverwendbare Layouts.
Rails-Vergleich: yield in Layouts
In Rails-Layouts nutzt du <%= yield %>, um den Seiteninhalt einzufügen. Benannte Slots entsprechen <%= yield :sidebar %> mit content_for :sidebar. Gleiche Idee, mehr Flexibilität.
Der Standard-Slot
Der einfachste Fall: Ein <slot /> in der Kind-Komponente wird durch den Inhalt ersetzt, den die Eltern-Komponente dazwischen schreibt.
<template>
<div class="alert-box">
<strong>⚠️ Achtung:</strong>
<slot />
</div>
</template><template>
<AlertBox>
<p>Das Formular enthält ungültige Daten.</p>
</AlertBox>
<!-- Alles zwischen den Tags wird in <slot /> eingefügt -->
</template>Fallback-Inhalt
Du kannst Standard-Inhalte definieren, die angezeigt werden, wenn die Eltern-Komponente keinen Inhalt übergibt.
<template>
<button class="btn" type="submit">
<!-- Fallback-Inhalt, wenn nichts übergeben wird -->
<slot>Absenden</slot>
</button>
</template>
<!-- Verwendung: -->
<!-- <SubmitButton /> → zeigt "Absenden" -->
<!-- <SubmitButton>Jetzt kaufen</SubmitButton> → zeigt "Jetzt kaufen" -->Benannte Slots
Wenn du mehrere Inhaltsbereiche brauchst, gibst du jedem Slot einen Namen. Die Eltern-Komponente füllt sie mit v-slot: oder der Kurzform #.
<template>
<div class="page-layout">
<header>
<slot name="header" />
</header>
<main>
<!-- Unbenannt = default slot -->
<slot />
</main>
<aside>
<slot name="sidebar">
<!-- Fallback -->
<p>Keine Sidebar definiert.</p>
</slot>
</aside>
<footer>
<slot name="footer" />
</footer>
</div>
</template><template>
<PageLayout>
<template #header>
<h1>Meine Seite</h1>
<nav>...</nav>
</template>
<!-- Default-Slot (kein #-Name nötig) -->
<article>
<p>Hauptinhalt der Seite.</p>
</article>
<template #sidebar>
<WidgetList />
</template>
<template #footer>
<p>© 2025 Mein Projekt</p>
</template>
</PageLayout>
</template>Kurzschreibweise
v-slot:header kann als #header geschrieben werden. Der Default-Slot heißt #default.
Scoped Slots — Daten zurückgeben
Scoped Slots geben Daten von der Kind-Komponente an den Slot-Inhalt zurück. So kann die Eltern-Komponente entscheiden, wie die Daten dargestellt werden, während die Kind-Komponente die Daten bereitstellt.
<script setup lang="ts">
interface User {
id: number
name: string
role: string
}
defineProps<{
users: User[]
}>()
</script>
<template>
<ul>
<li v-for="user in users" :key="user.id">
<!-- Daten an den Slot zurückgeben -->
<slot :user="user" :is-admin="user.role === 'admin'">
<!-- Fallback-Darstellung -->
{{ user.name }}
</slot>
</li>
</ul>
</template><template>
<UserList :users="users">
<!-- Destrukturierung der Slot-Props -->
<template #default="{ user, isAdmin }">
<div class="user-card">
<strong>{{ user.name }}</strong>
<span v-if="isAdmin" class="badge">Admin</span>
</div>
</template>
</UserList>
</template>Renderless Components
Das Scoped-Slot-Pattern wird für Renderless Components genutzt — Komponenten, die nur Logik bereitstellen und kein eigenes HTML rendern. Beispiel: Eine <MouseTracker>-Komponente, die x/y über einen Scoped Slot verfügbar macht.
Interaktive Demo
Eine flexible Karten-Komponente mit Header-, Body- und Footer-Slots. Schalte die einzelnen Bereiche an und ab:
#header
#default
Hier steht der Hauptinhalt der Karte. Dieser Bereich ist immer sichtbar, weil er den Standard-Slot nutzt.
Zusammenfassung
Kernpunkte
<slot />projiziert Eltern-Inhalt in Kind-Komponenten- Benannte Slots (
#header,#footer) für mehrere Bereiche - Scoped Slots geben Daten an den Slot-Inhalt zurück
- Fallback-Inhalte werden angezeigt, wenn kein Inhalt übergeben wird
- Renderless Components nutzen Scoped Slots für reine Logik-Komponenten