Slot adalah lubang di template child yang diisi parent dengan markup. Props mengirim data, seperti teks dan angka. Slot mengirim potongan template utuh: judul, paragraf, tombol. Bayangin nampan saji Warung Bu Sri yang punya sekat-sekat berlabel "judul", "isi", dan "aksi". Bentuk nampannya selalu sama, tapi yang menaruh makanan di tiap sekat adalah yang menyajikan. Sekat yang dibiarkan kosong menampilkan kertas cadangan, misalnya "Menu belum diisi". Nampan itu komponen child Kartu, sekatnya slot, dan yang menyajikan parent-nya.
🧪 Tebakan cepat:
Kartupunya<slot name="judul">dan<slot>. Parent menulis<h3>Nasi Goreng</h3>langsung di antara<Kartu>dan</Kartu>, tanpa<template #judul>. Judulnya masuk slot yang mana? Tebak dulu, jawabannya ada di bagian "Named slot: banyak lubang".
Default slot: satu lubang
<!-- Kartu.vue -->
<template>
<div class="kartu">
<slot></slot>
</div>
</template>
<!-- App.vue -->
<Kartu>
<h3>Nasi Goreng</h3>
<p>Rp15000</p>
</Kartu>
HTML yang keluar:
<div class="kartu"><h3>Nasi Goreng</h3><p>Rp15000</p></div>
<slot></slot>, atau<slot />, menandai tempatnya. Semua yang ditulis parent di antara<Kartu>dan</Kartu>mendarat di sana.- Isi slot ditulis di template parent, jadi dia membaca data parent.
{{ harga }}di dalam<Kartu>adalahhargamilik App, bukan props milikKartu.
Fallback content: isi cadangan
<!-- Kartu.vue -->
<div class="kartu">
<slot>Menu belum diisi</slot>
</div>
| Parent menulis | Yang tampil |
|---|---|
<Kartu><p>Es Teh</p></Kartu> |
Es Teh |
<Kartu /> |
Menu belum diisi |
Isi di antara <slot> dan </slot> disebut fallback content. Dia cuma tampil kalau parent gak mengirim apa-apa. Child dengan <slot>Default</slot> yang gak diisi menampilkan Default.
Named slot: banyak lubang
<!-- Kartu.vue -->
<template>
<div class="kartu">
<header><slot name="judul">Tanpa judul</slot></header>
<main><slot></slot></main>
<footer><slot name="aksi"></slot></footer>
</div>
</template>
<!-- App.vue -->
<Kartu>
<template #judul><h3>Nasi Goreng</h3></template>
<p>Rp15000</p>
<template #aksi><button>Tambah</button></template>
</Kartu>
HTML yang keluar:
<div class="kartu">
<header><h3>Nasi Goreng</h3></header>
<main><p>Rp15000</p></main>
<footer><button>Tambah</button></footer>
</div>
<slot name="judul">memberi nama lubangnya. Slot tanpa nama namanyadefault.- Parent mengisi slot bernama dengan
<template #judul>.#juduladalah singkatanv-slot:judul. - Isi yang gak dibungkus
<template #...>masuk ke default slot. Jadi<h3>yang ditulis langsung mendarat di<main>, dan<header>menampilkanTanpa judul. Itu jawaban tebakannya. - Atribut
slot="judul"dari Vue 2 gak berlaku lagi di Vue 3. Elemennya tetap masuk default slot.
📌 Scoped slot: child meminjamkan data
Kadang child yang punya datanya, tapi parent yang mau menentukan tampilannya. Child mengirim data lewat atribut di <slot>:
<!-- DaftarMenu.vue -->
<script setup>
const menu = [
{ nama: "Nasi Goreng", harga: 15000 },
{ nama: "Mie Ayam", harga: 12000 },
{ nama: "Es Teh", harga: 5000 },
]
</script>
<template>
<ul>
<li v-for="m in menu" :key="m.nama">
<slot :item="m">{{ m.nama }}</slot>
</li>
</ul>
</template>
<!-- App.vue -->
<DaftarMenu v-slot="{ item }">
<b>{{ item.nama }}</b> Rp{{ item.harga }}
</DaftarMenu>
Hasilnya tiga <li>: Nasi Goreng Rp15000, Mie Ayam Rp12000, Es Teh Rp5000. v-slot="{ item }" mengambil item yang dikirim child. Untuk slot bernama: <template #baris="{ item }">.
File .vue dan editor latihan
Di editor, child ditulis sebagai objek dan dipasang dengan <component :is>. Isi slot ditulis di antara <component> dan </component>:
const Kartu = {
template: `
<div class="kartu">
<header><slot name="judul">Tanpa judul</slot></header>
<main><slot>Menu belum diisi</slot></main>
</div>
`,
}
---template---
<div>
<component :is="Kartu">
<template #judul><h3>Nasi Goreng</h3></template>
<p>Rp15000</p>
</component>
<component :is="Kartu" />
</div>
| Di file .vue | Di editor latihan |
|---|---|
<slot> di <template> milik Kartu.vue |
<slot> di opsi template objek Kartu |
<Kartu>...</Kartu> |
<component :is="Kartu">...</component> |
Kedua latihannya memakai bentuk ini. Pemeriksanya membaca HTML hasilnya (.card p berisi Konten kartu, lalu .panel-header berisi Profil dan .panel-body berisi Data pengguna) dan juga <slot> di template child, <template #header> di parent, dan bahwa <div> kartu dan panelnya ditulis di template child.
⚠️ Jebakan yang sering ketemu
- Isi named slot tanpa
<template #nama>. Isinya masuk default slot. Bungkus dengan<template #judul>. - Mengira isi slot membaca data child. Isi slot ditulis di parent dan membaca data parent. Data child dipinjam lewat scoped slot.
- Lupa
<slot>di child. Tanpa<slot>, apa pun yang dikirim parent dibuang tanpa pesan. - Elemen di tempat yang salah. Latihan mencari
.card p:<p>harus berada di dalam elemen berkelascard, bukan di sebelahnya.
TL;DR: Slot adalah lubang di template child yang diisi parent dengan markup. <slot /> adalah default slot, isi di antara tag komponen mendarat di sana. Isi di antara <slot> dan </slot> adalah fallback content yang tampil kalau parent gak mengisi apa-apa. <slot name="judul"> membuat named slot yang diisi dengan <template #judul> (singkatan v-slot:judul); isi tanpa <template #...> masuk default slot. Isi slot membaca data parent, dan scoped slot (<slot :item="m"> dengan v-slot="{ item }") meminjamkan data child ke parent.