Slot adalah lubang di dalam komponen yang diisi markup dari induknya. Props mengirim nilai seperti teks dan angka; slot mengirim potongan tampilan utuh: paragraf, tombol, bahkan komponen lain. Bayangin nampan saji Warung Bu Sri. Semua nampan bentuknya sama: ada tempat kartu nama menu di atas, tempat piring di tengah, dan tempat sendok di bawah. Nampannya gak peduli piringnya berisi Nasi Goreng atau Es Teh; yang mengisi adalah pelayan yang memakainya. Kalau tempat piringnya kosong, di dasar nampan tercetak tulisan "Belum ada isi". Komponen Kartu adalah nampannya, tiap <slot> adalah tempatnya, induk adalah pelayannya, dan tulisan tercetak itu fallback.
🧪 Tebakan cepat:
Kartu.sveltemembungkus<slot name="aksi" />dengan<footer>yang punya garis atas. Kartu Es Teh dipakai tanpa tombol aksi. Garis footer-nya ikut hilang, atau tetap tergambar kosong? Jawabannya ada di bagian "Slot yang gak diisi dan $slots".
Slot default: <slot />
Kartu.svelte cuma menyediakan bingkainya. Isinya datang dari induk:
<!-- Kartu.svelte -->
<div class="kartu">
<slot />
</div>
<!-- App.svelte -->
<script>
import Kartu from "./Kartu.svelte"
</script>
<Kartu>
<h3>Nasi Goreng</h3>
<p>Rp 15.000</p>
</Kartu>
Hasilnya <div class="kartu"><h3>Nasi Goreng</h3> <p>Rp 15.000</p></div>.
<slot />menandai tempat isi kiriman induk dipasang. Slot tanpa nama ini disebut slot default.- Isinya ditulis di antara
<Kartu>dan</Kartu>, seperti isi tag HTML biasa.<Kartu />tanpa tag penutup berarti gak mengirim isi apa pun. - Isi kiriman tetap milik induk: variabelnya dibaca dari script App, dan style-nya diambil dari
<style>App, bukan dari Kartu.
Fallback: isi cadangan
<div class="kartu">
<slot>Belum ada isi</slot>
</div>
| Ditulis induk | Tampil di kartu |
|---|---|
<Kartu><p>Rp 15.000</p></Kartu> |
Rp 15.000 |
<Kartu /> |
Belum ada isi |
<Kartu> </Kartu> |
Belum ada isi |
- Isi di antara
<slot>dan</slot>adalah fallback, konten cadangan. Dia tampil cuma kalau induknya gak mengirim apa-apa. Spasi doang gak dihitung sebagai kiriman. - Fallback butuh tag pembuka dan tag penutup.
<slot />tag tunggal gak punya tempat untuk fallback. - Ini bentuk latihan pertama:
<div class="card"><slot>Konten default</slot></div>. Latihannya merender Card tanpa induk, jadi yang tampilKonten default.
Named slot: lebih dari satu lubang
<!-- Kartu.svelte -->
<div class="kartu">
<slot name="judul"><h3>Menu baru</h3></slot>
<slot>Belum ada isi</slot>
<footer>
<slot name="aksi" />
</footer>
</div>
<!-- App.svelte -->
<Kartu>
<h3 slot="judul">Nasi Goreng</h3>
<p>Rp 15.000</p>
<button slot="aksi">Pesan</button>
</Kartu>
<slot name="judul">membuat lubang bernamajudul. Di induk, elemen dengan atributslot="judul"masuk ke lubang itu.- Elemen tanpa atribut
slot, di sini<p>Rp 15.000</p>, masuk ke slot default. - Urutan tampilnya mengikuti urutan
<slot>di Kartu, bukan urutan tulisan di App. - Named slot juga boleh punya fallback:
<h3>Menu baru</h3>tampil kalau App gak mengirim judul. - Butuh dua tombol di satu lubang tanpa pembungkus? Tulis
<svelte:fragment slot="aksi">...</svelte:fragment>. - Latihan kedua memakai pola ini:
<div class="alert"><h3><slot name="title">Peringatan</slot></h3><slot>Tidak ada pesan.</slot></div>.
Slot yang gak diisi dan $slots
<Kartu><h3 slot="judul">Es Teh</h3></Kartu> menghasilkan:
<div class="kartu"><h3 slot="judul">Es Teh</h3> Belum ada isi <footer></footer></div>
(Atribut slot="judul" ikut terbawa ke HTML-nya, dan gak mengubah tampilannya.)
<footer> adalah markup milik Kartu, bukan bagian dari slot. Slot aksi yang gak diisi cuma kosong, sedangkan pembungkusnya tetap ada, lengkap dengan garis atasnya. Jadi garisnya tetap tergambar. Itu jawaban tebakannya.
$slots adalah objek yang Svelte berikan ke tiap komponen, berisi nama slot yang benar-benar diisi induknya. Untuk kartu Es Teh di atas, isinya { judul: true }. Pakai untuk menyembunyikan pembungkusnya:
{#if $slots.aksi}
<footer>
<slot name="aksi" />
</footer>
{/if}
Kalau induk mengirim slot="harga" padahal Kartu gak punya slot harga, isinya dibuang, dan mode dev memberi warning <Kartu> received an unexpected slot "harga".
Slot props: anak mengoper data balik
Kadang anaknya yang punya data, tapi induknya yang menentukan tampilannya. DaftarMenu.svelte mengulang array menu, dan tiap putaran mengoper satu item lewat slot:
<!-- DaftarMenu.svelte -->
<script>
const menu = [
{ nama: "Nasi Goreng", harga: 15000 },
{ nama: "Mie Ayam", harga: 12000 },
{ nama: "Es Teh", harga: 5000 },
]
</script>
<ul>
{#each menu as m}
<li><slot item={m}>{m.nama}</slot></li>
{/each}
</ul>
<!-- App.svelte -->
<DaftarMenu let:item>
<b>{item.nama}</b> Rp {item.harga.toLocaleString("id-ID")}
</DaftarMenu>
| Ditulis induk | Baris pertama |
|---|---|
<DaftarMenu /> |
Nasi Goreng (fallback) |
<DaftarMenu let:item>...</DaftarMenu> |
Nasi Goreng Rp 15.000 |
<slot item={m}>mengopermdengan namaitem. Nilai yang dioper lewat slot seperti ini disebut slot prop.let:itemdi induk menerima nilai itu.let:item={menu}menerimanya dengan nama lain,menu.- Tanpa
let:item,itemgak dikenal di App: compiler memberi warning'item' is not defined, lalu saat jalan munculReferenceError: item is not defined.
Svelte 5: snippet dan {@render}
Di Svelte 5, <slot> masih jalan di komponen non-runes. Di komponen runes, compiler memberi warning bahwa <slot> sudah usang. Gantinya snippet:
<!-- Kartu.svelte, Svelte 5 -->
<script>
let { judul, children, aksi } = $props()
</script>
<div class="kartu">
{#if judul}{@render judul()}{:else}<h3>Menu baru</h3>{/if}
{#if children}{@render children()}{:else}Belum ada isi{/if}
{#if aksi}<footer>{@render aksi()}</footer>{/if}
</div>
Di App, judul dikirim dengan {#snippet judul()}<h3>Nasi Goreng</h3>{/snippet} di antara tag Kartu. Isi biasa jadi prop children, named slot jadi snippet bernama, fallback jadi {:else}, dan $slots.aksi cukup diganti aksi. Slot prop jadi parameter snippet: {#snippet baris(item)} di induk, {@render baris(m)} di anak. Latihannya di-compile dengan Svelte 4, jadi tulis <slot>.
⚠️ Jebakan yang sering ketemu
- Fallback di luar tag slot.
<slot />Konten defaultmenaruh teksnya di samping lubang, jadi teks itu selalu tampil walau induk mengirim isi. Fallback ditulis di antara<slot>dan</slot>; latihan pertama memeriksa bentuk itu. slot="..."terlalu dalam.<Kartu><div><h3 slot="judul">ditolak compiler:Element with a slot='...' attribute must be a child of a component or a descendant of a custom element. Atributslotharus ada di anak langsung komponennya.- Mengira slot kosong ikut menghapus pembungkusnya.
<footer>tetap ada. Bungkus dengan{#if $slots.aksi}. - Style di anak untuk isi kiriman.
p { color: red; }di Kartu gak mengenai<p>kiriman App, dan compiler memberi warningUnused CSS selector "p". Atur style isi kiriman di App.
TL;DR: Slot adalah lubang di komponen yang diisi markup dari induk. <slot /> adalah slot default; isinya ditulis di antara <Kartu> dan </Kartu>. Isi di antara <slot> dan </slot> adalah fallback yang tampil cuma kalau induk gak mengirim apa-apa. <slot name="judul"> membuat named slot, diisi dengan slot="judul" di anak langsung komponen. Slot yang gak diisi gak menghapus pembungkusnya; $slots.aksi memberi tahu apakah slot itu diisi. <slot item={m}> mengoper data ke induk, yang menerimanya dengan let:item. Di Svelte 5, slot diganti snippet dan {@render children()}. Latihannya: <div class="card"><slot>Konten default</slot></div>, dan Alert dengan <h3><slot name="title">Peringatan</slot></h3> plus <slot>Tidak ada pesan.</slot>.