Slots Svelte — Svelte

Slots memungkinkan komponen menerima konten dari parent — seperti children di React. Default Slot Konten di dalam card Fallback Content Konten default jika…

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.svelte membungkus <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>.

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

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 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

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

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>.

Yang akan kamu pelajari