Day 3 · Blade Components

Day 3 — Blade Components & Layout Lanjutan

Setelah menguasai sintaks dan layout Blade dasar, hari ini kita merapikan potongan antarmuka yang berulang menjadi component yang konsisten, mudah dipakai ulang, dan mudah dirawat.

⏱️ 3–4 jam🧩 Reusable UI🌱 Pemula → rapi🎯 Hasil: component card

Target Belajar Hari Ini

Pada Day 2 kita sudah memakai {{ }}, @if, @foreach, serta layout dengan @extends dan @section. Fondasi itu tetap dipakai; hari ini kita naik satu tingkat dengan memecah tampilan menjadi bagian yang memiliki nama dan tanggung jawab jelas.

Tujuan utamanya bukan sekadar membuat file lebih banyak. Kita ingin perubahan tampilan cukup dilakukan di satu tempat, sedangkan halaman yang memakai tampilan tersebut langsung mendapat hasil yang konsisten.

  • Memahami apa itu Blade Component dan alasan fitur ini ada.
  • Mengetahui kapan memakai Component dan kapan cukup memakai @include.
  • Membuat class-based component dan anonymous component.
  • Mengirim data melalui props serta isi melalui default dan named slot.
  • Mengelola component agar tetap mudah ditemukan ketika jumlahnya bertambah.

Selesai materi ini, kamu diharapkan bisa melihat HTML yang berulang lalu memilih bentuk abstraksi yang tepat—bukan otomatis mengubah semua potongan HTML menjadi component.

⚠️ Kesalahan umum: mengejar hafalan sintaksJangan hanya menghafal bentuk <x-alert>. Mulailah dari pertanyaan: “bagian mana yang berulang, data apa yang berubah, dan isi apa yang perlu fleksibel?” Dari jawaban itulah props dan slot ditentukan.
📌 Kapan fokus pada Component?Pakai materi ini ketika tampilan yang sama muncul di beberapa halaman atau memiliki aturan tampilan sendiri. Jangan buru-buru membuat component untuk satu tag HTML sederhana yang hanya dipakai sekali.

Kenapa perlu Component

Aplikasi nyata cepat dipenuhi pola HTML yang sama: alert, tombol, kartu siswa, badge status, dan kotak form. Jika setiap halaman menyalin markup tersebut, satu perubahan kelas CSS harus diedit berkali-kali dan hasil akhirnya mudah tidak konsisten.

Bayangkan component sebagai cetakan kue. Kita membuat bentuk cetakan sekali, lalu mengganti adonan atau hiasannya untuk setiap kue. Tanpa cetakan, kita membentuk setiap kue secara manual; selama jumlahnya dua mungkin terasa mudah, tetapi puluhan kue akan berbeda ukuran dan menyita waktu.

Sebelum memakai component, halaman menyalin seluruh struktur alert. Perhatikan bahwa yang sebenarnya berubah hanya jenis dan pesannya.

Sebelum — markup disalin di banyak halaman
<!-- halaman profil -->
<div class="alert alert-success">
    Profil berhasil disimpan.
</div>

<!-- halaman kelas -->
<div class="alert alert-danger">
    Kelas gagal dihapus.
</div>

Sesudah dibuat sebagai component, pemanggil menyatakan maksud, sementara detail HTML berada di satu file. Ketika desain alert berubah, kita mengedit component sekali saja.

Sesudah — intent lebih jelas
<x-alert type="success">
    Profil berhasil disimpan.
</x-alert>

<x-alert type="danger">
    Kelas gagal dihapus.
</x-alert>
⚠️ Kesalahan umum: membuat component terlalu kecilMembungkus setiap <p> atau <div> menjadi component justru menyulitkan pembacaan. Perbaiki dengan mengekstrak pola yang benar-benar berulang, punya makna UI, atau memiliki variasi yang jelas.
📌 Pakai vs tidakPakai component untuk elemen reusable seperti alert, card, modal, dan input yang punya kontrak props. Tetap tulis HTML biasa jika markup hanya sekali dipakai, sangat spesifik pada satu halaman, dan belum memiliki pola stabil.

Blade Component Class-based

Class-based component terdiri dari class PHP dan view Blade. Class menjadi tempat menerima serta menyiapkan data, sedangkan view fokus menghasilkan HTML. Pemisahan ini cocok ketika component memiliki nilai default, transformasi data, atau logika presentasi yang mulai berarti.

Buat component bernama Alert melalui Artisan. Laravel akan menghasilkan dua file yang saling berpasangan.

Terminal dan struktur hasil
php artisan make:component Alert

# File yang dibuat:
app/View/Components/Alert.php
resources/views/components/alert.blade.php

Data yang ditulis sebagai atribut pada <x-alert> masuk ke constructor publik. Properti publik $type kemudian tersedia di view component.

app/View/Components/Alert.php
<?php

namespace App\View\Components;

use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;

class Alert extends Component
{
    public function __construct(
        public string $type = 'info',
    ) {}

    public function render(): View|Closure|string
    {
        return view('components.alert');
    }
}
resources/views/components/alert.blade.php + pemakaian
<!-- view component -->
<div class="alert alert-{{ $type }}">
    {{ $slot }}
</div>

<!-- di halaman mana pun -->
<x-alert type="success">
    Data siswa berhasil disimpan.
</x-alert>

Nama class Alert otomatis dipanggil dengan tag kebab-case <x-alert>. Untuk nama bertingkat seperti FormInput, tag lazimnya menjadi <x-form-input>.

⚠️ Kesalahan umum: lupa mengembalikan view di render()Method render() harus mengembalikan view, closure, atau string HTML. Jika hanya menulis view('components.alert'); tanpa return, component tidak punya hasil render. Perbaiki menjadi return view('components.alert');.
📌 Kapan memilih class-based?Pilih class-based ketika data perlu dinormalisasi, divalidasi secara presentasional, atau dihitung sebelum tampil. Untuk markup sederhana yang hanya menerima beberapa props, anonymous component lebih ringkas.

Anonymous Component

Anonymous component adalah versi ringan yang hanya membutuhkan file Blade, tanpa class PHP pasangan. Laravel mencari tag <x-badge> di resources/views/components/badge.blade.php.

Karena tidak ada constructor, kontrak input dideklarasikan dengan @props di awal file. Nilai di sebelah kanan menjadi default jika atribut tidak dikirim oleh pemanggil.

resources/views/components/badge.blade.php
@props([
    'color' => 'gray',
])

<span class="badge badge-{{ $color }}">
    {{ $slot }}
</span>

Pemanggilannya tetap memakai sintaks x- yang sama, sehingga halaman tidak perlu tahu apakah implementasinya class-based atau anonymous. Detail tersebut dapat diubah kemudian tanpa mengganti cara pemakaian selama kontrak props tetap sama.

resources/views/siswa/index.blade.php
<x-badge color="green">Aktif</x-badge>
<x-badge>Belum diverifikasi</x-badge>

Anonymous component juga dapat menerima atribut HTML tambahan melalui $attributes. Dengan merge(), class dasar tetap ada sementara pemanggil boleh menambahkan class lain.

Meneruskan atribut tambahan
<span {{ $attributes->merge(['class' => 'badge badge-' . $color]) }}>
    {{ $slot }}
</span>

<x-badge color="green" class="text-sm">Aktif</x-badge>
⚠️ Kesalahan umum: lupa @propsTanpa deklarasi @props(['color' => 'gray']), variabel $color belum tentu tersedia seperti yang diharapkan dan atribut dapat tercampur dengan attribute bag. Taruh @props pada baris awal component serta berikan default yang aman.
📌 Kapan memilih anonymous?Pakai anonymous component untuk presentasi sederhana seperti badge, button, card, atau empty state. Jangan memaksakan logika PHP panjang di file Blade; ketika persiapan data membesar, pindahkan ke class-based component.

@include untuk Partial Views

@include menyisipkan isi satu file Blade ke file lain. Ini cocok untuk partial view: fragmen seperti header, footer, atau potongan navigasi yang tidak memerlukan API component formal.

Berbeda dari component, partial tidak dipanggil memakai tag <x-...>, tidak memiliki slot, dan biasanya lebih bergantung pada konteks halaman. Ia adalah cara sederhana untuk memecah file panjang, bukan selalu sebuah unit UI mandiri.

resources/views/partials/header.blade.php
<header>
    <h1>{{ $judul }}</h1>
</header>
resources/views/partials/footer.blade.php + halaman
<!-- partial footer -->
<footer>&copy; {{ date('Y') }} Kelas Laravel</footer>

<!-- pemakaian pada halaman -->
@include('partials.header', [
    'judul' => 'Daftar Siswa',
])

<main>...</main>

@include('partials.footer')

Laravel memang meneruskan variabel yang tersedia pada view parent ke partial saat @include dijalankan. Namun, mengandalkan variabel implisit membuat partial rapuh: begitu dipanggil dari parent lain yang tidak memiliki variabel tersebut, muncul error undefined variable. Mengirim input secara eksplisit membuat kebutuhan partial terlihat.

Jadi, anggap array argumen kedua sebagai dokumentasi mini. Pembaca langsung tahu bahwa header memerlukan $judul, tanpa harus menelusuri seluruh variabel milik parent.

⚠️ Kesalahan umum: menganggap variabel parent selalu otomatis tersediaVariabel hanya tersedia jika memang ada pada parent saat partial dipanggil; parent lain mungkin tidak memilikinya. Perbaiki dengan passing data eksplisit: @include('partials.header', ['judul' => $judulHalaman]), atau gunakan nilai default bila sesuai.
📌 Component vs @includePakai @include untuk potongan struktural sederhana yang erat dengan halaman, seperti header dan footer. Pakai component jika fragmen merupakan elemen reusable formal, punya props/slot, butuh attribute forwarding, atau ingin kontrak pemakaian yang konsisten.

Props & Slot

Props adalah data bernama yang mengatur component, misalnya warna, judul, atau status. Slot adalah ruang untuk menyisipkan isi dari pemanggil. Analogi sederhananya: props adalah pengaturan bingkai foto, sedangkan slot adalah foto yang dimasukkan ke dalam bingkai.

Slot tanpa nama disebut default slot dan tersedia sebagai $slot. Pada component kartu berikut, props $variant mengatur gaya, sedangkan isi paragraf dari pemanggil masuk ke area body.

resources/views/components/card.blade.php
@props([
    'variant' => 'default',
])

<article {{ $attributes->merge([
    'class' => 'card card-' . $variant,
]) }}>
    <div class="card-body">
        {{ $slot }}
    </div>
</article>

Jika component membutuhkan beberapa area isi, gunakan named slot. Contohnya, judul masuk ke slot $title, sementara isi utama tetap masuk ke $slot.

Card dengan named slot dan default slot
<!-- components/card.blade.php -->
@props(['variant' => 'default'])

<article class="card card-{{ $variant }}">
    <header class="card-title">{{ $title }}</header>
    <div class="card-body">{{ $slot }}</div>
</article>

<!-- pemakaian -->
<x-card variant="highlight">
    <x-slot:title>Profil Siswa</x-slot:title>

    <p>Nama: {{ $siswa->nama }}</p>
</x-card>

Atribut statis ditulis seperti HTML biasa, misalnya variant="highlight". Untuk mengirim ekspresi PHP atau variabel, gunakan awalan titik dua, misalnya :variant="$warnaKartu"; tanpa titik dua nilainya dianggap string literal.

Prop statis vs prop dinamis
<!-- string tetap -->
<x-card variant="highlight">...</x-card>

<!-- ekspresi/variabel PHP -->
<x-card :variant="$warnaKartu">...</x-card>
⚠️ Kesalahan umum: lupa menampilkan {{ $slot }}Isi di antara tag pembuka dan penutup tidak muncul otomatis. View component harus menentukan posisinya dengan {{ $slot }}; named slot juga harus dicetak, misalnya {{ $title }}.
📌 Props vs slotPakai props untuk nilai konfigurasi yang singkat dan terstruktur—status, ukuran, warna, atau ID. Pakai slot untuk konten HTML yang panjang atau fleksibel. Jangan mengirim satu blok HTML besar sebagai string prop karena escaping dan keterbacaannya lebih sulit dikelola.

Mengelola Component

Membuat component pertama biasanya mudah. Tantangan sebenarnya muncul setelah aplikasi membesar: file sulit ditemukan, variasi tampilan mulai disalin, dan logika menumpuk di Blade. Enam kasus berikut membantu menjaga component tetap sebagai alat perapihan—bukan sumber kerumitan baru.

Kasus 1 — Component makin banyak, folder berantakan

Masalah: semua file disimpan langsung di resources/views/components/. Saat jumlahnya puluhan, nama seperti input.blade.php, card.blade.php, dan header.blade.php tidak lagi menjelaskan konteksnya.

Solusi: kelompokkan berdasarkan keluarga UI, bukan berdasarkan halaman pemakai. Misalnya input form masuk ke form/, variasi kartu ke card/, dan kerangka halaman ke layout/. Subfolder menjadi bagian nama tag component.

Struktur yang mudah dipindai
resources/views/components/
├── form/
│   ├── input.blade.php       # <x-form.input>
│   └── select.blade.php      # <x-form.select>
├── card/
│   ├── siswa.blade.php       # <x-card.siswa>
│   └── kelas.blade.php       # <x-card.kelas>
└── layout/
    └── panel.blade.php       # <x-layout.panel>

Gunakan nama yang menyatakan peran, bukan warna sesaat: alert.blade.php dengan prop type lebih tahan lama daripada file terpisah alert-merah.blade.php. Hindari folder terlalu dalam; satu atau dua tingkat biasanya cukup.

📌 Aturan praktisJika kamu tidak bisa menebak lokasi file dari tag <x-card.siswa>, struktur atau penamaannya perlu disederhanakan.

Kasus 2 — Component butuh CSS sendiri

Masalah: component punya gaya khusus, tetapi menaruh <style> di setiap pemanggilan membuat CSS tercetak berulang. Class umum seperti .title juga mudah menimpa elemen lain.

Solusi: untuk contoh sangat kecil atau component yang hanya muncul sekali, class utility/inline dapat diterima. Untuk component reusable, taruh CSS di file asset seperti resources/css/components/alert.css, lalu import satu kali dari entry CSS utama yang dibangun Vite. Beri nama class terisolasi dengan pola seperti .c-alert dan .c-alert__title.

CSS terpisah dan diimpor sekali
/* resources/css/app.css */
@import './components/alert.css';

/* resources/css/components/alert.css */
.c-alert { padding: 1rem; border-radius: .5rem; }
.c-alert--success { background: #dcfce7; }
.c-alert__title { font-weight: 700; }

Jangan menambahkan tag <link> dari dalam setiap card. Asset sebaiknya dimuat oleh layout melalui @vite. Jika memang ada CSS yang hanya diperlukan satu halaman, gunakan stack layout seperti @push('styles') secara sadar dan pastikan tidak didorong berulang.

⚠️ Hindari konflik globalNama .card, .header, atau .active terlalu umum. Pakai prefix component atau utility framework secara konsisten; jangan memakai !important sebagai obat pertama.

Kasus 3 — Dipakai di banyak halaman, tetapi tampilannya sedikit berbeda

Masalah: card yang sama memerlukan badge di halaman daftar, tombol di halaman detail kelas, dan kadang tanpa footer. Menduplikasi menjadi siswa-card-a dan siswa-card-b membuat perbaikan markup harus dilakukan dua kali.

Solusi: pertahankan satu component. Gunakan prop opsional dengan nilai default untuk variasi kecil yang terstruktur, serta named slot untuk blok HTML fleksibel. Default membuat pemanggilan sederhana tetap ringkas.

Component dengan prop default dan slot opsional
<!-- components/card/siswa.blade.php -->
@props(['compact' => false])

<article class="student-card {{ $compact ? 'student-card--compact' : '' }}">
    <h3>{{ $nama }}</h3>
    {{ $slot }}

    @isset($actions)
        <footer>{{ $actions }}</footer>
    @endisset
</article>

<!-- pemakaian -->
<x-card.siswa nama="Alya" :compact="true">
    <x-slot:actions><a href="/siswa/1">Detail</a></x-slot:actions>
</x-card.siswa>

Jika prop boolean mulai menjadi lima atau enam sakelar yang saling memengaruhi, kontrak component sudah terlalu rumit. Pertimbangkan satu prop variant yang jelas atau pecah component berdasarkan tanggung jawab yang benar-benar berbeda.

Kasus 4 — Component perlu logic PHP agak rumit

Masalah: anonymous component mulai berisi banyak @php: memilih ikon, menghitung label, memformat data, dan menentukan beberapa class. View sulit dibaca dan sulit diuji.

Solusi: anonymous component tetap tepat bila hanya memiliki props, default, kondisi singkat, dan markup. Naikkan menjadi class-based component ketika perlu normalisasi data, method bantu, dependency injection, atau perhitungan presentasi yang digunakan beberapa kali.

Pindahkan persiapan data ke class
class StudentCard extends Component
{
    public function __construct(public Student $student) {}

    public function statusLabel(): string
    {
        return $this->student->active ? 'Aktif' : 'Nonaktif';
    }
}

Component class tetap seharusnya menangani logika presentasi. Query bisnis besar, perubahan database, atau aturan domain bukan tanggung jawab component; siapkan data di controller/service lalu kirimkan ke view.

📌 Batas sederhanaJika Blade bisa dipahami dari atas ke bawah tanpa berhenti membaca blok PHP panjang, anonymous component masih cukup. Jika view lebih banyak “menghitung” daripada “menampilkan”, pindahkan persiapannya.

Kasus 5 — Component dipanggil tetapi data salah atau kosong

Masalah: nama siswa kosong, muncul undefined variable, prop selalu berupa teks, atau Laravel mengatakan component tidak ditemukan. Debug secara berurutan agar tidak menebak-nebak.

  1. Periksa pemanggilan. Pastikan prop memang dikirim. <x-card.siswa :siswa="$siswa" /> mengirim variabel, sedangkan siswa="$siswa" mengirim string literal.
  2. Cocokkan nama. Prop student-name dipetakan ke variabel $studentName, bukan $student_name. Nama di @props atau constructor harus sama dengan yang dipakai view.
  3. Inspeksi data sementara. Gunakan @dump($siswa) atau dd($siswa) di controller/component saat lokal. Hapus kembali setelah selesai dan jangan melakukan dump data sensitif di production.
  4. Periksa lokasi file dan tag. resources/views/components/card/siswa.blade.php dipanggil sebagai <x-card.siswa>. Pastikan ekstensi tepat .blade.php, huruf/nama folder cocok, dan bukan tersimpan di resources/view/.
  5. Bersihkan cache bila file baru masih tidak terdeteksi. Jalankan php artisan view:clear dan, bila perlu, php artisan optimize:clear.
Dua pemanggilan yang berbeda arti
<!-- benar: nilai variabel PHP -->
<x-card.siswa :nama="$siswa->nama" />

<!-- salah bila yang diinginkan isi variabel -->
<x-card.siswa nama="$siswa->nama" />
⚠️ Jangan langsung menyalahkan componentData mungkin sudah kosong sejak controller. Telusuri alurnya: controller → view pemanggil → prop/constructor → view component. Cek satu batas pada satu waktu.

Kasus 6 — Kapan jangan membuat component

Masalah: keinginan “membuat semuanya reusable” menghasilkan terlalu banyak file dan tag abstrak. Pembaca harus bolak-balik membuka file hanya untuk memahami tiga baris HTML yang dipakai sekali.

Solusi: jangan buat component jika markup hanya dipakai sekali, sangat spesifik pada satu halaman, atau bentuknya belum stabil. Tulis HTML langsung terlebih dahulu. Jika tujuanmu hanya memecah file halaman yang panjang dan fragmen masih bergantung pada variabel parent, @include sering lebih jujur.

SituasiPilihanAlasan
Tiga baris profil khusus satu halamanHTML biasaBelum ada reuse atau kontrak.
Header/footer struktural sederhana@includeMemecah file tanpa API props/slot.
Alert/card dipakai lintas halamanComponentPunya pola, input, dan tampilan konsisten.
UI reusable dengan persiapan dataClass-based componentLogika presentasi dapat dipisah dari markup.
✅ Ekstrak setelah pola terlihatDuplikasi dua atau tiga kali bukan aturan mutlak, tetapi sinyal untuk membandingkan markup. Buat component ketika kamu sudah tahu bagian tetap, bagian berubah, serta nama props/slot yang masuk akal.

Praktik Mini — Alert, Header, dan Footer Sekolah

Kita akan membuat satu anonymous component <x-alert> untuk pesan reusable, lalu dua partial sederhana untuk kerangka halaman sekolah. Hasil akhirnya memperlihatkan kapan component dan @include bekerja berdampingan.

Langkah 1 — Siapkan file

Terminal
mkdir -p resources/views/components resources/views/partials
# Buat file berikut melalui editor:
resources/views/components/alert.blade.php
resources/views/partials/header.blade.php
resources/views/partials/footer.blade.php
resources/views/sekolah.blade.php

Langkah 2 — Buat component alert

Deklarasikan prop type dengan default info. $slot menjadi pesan yang ditulis di antara tag pembuka dan penutup.

resources/views/components/alert.blade.php
@props(['type' => 'info'])

<div {{ $attributes->merge([
    'class' => 'alert alert-' . $type,
]) }} role="alert">
    {{ $slot }}
</div>

Langkah 3 — Buat partial header dan footer

resources/views/partials/header.blade.php
<header class="site-header">
    <h1>{{ $namaSekolah }}</h1>
    <nav><a href="/">Beranda</a> · <a href="/siswa">Siswa</a></nav>
</header>
resources/views/partials/footer.blade.php
<footer>
    &copy; {{ date('Y') }} {{ $namaSekolah }}
</footer>

Langkah 4 — Gabungkan di halaman sekolah

resources/views/sekolah.blade.php
<!DOCTYPE html>
<html lang="id">
<body>
    @include('partials.header', ['namaSekolah' => 'SMP Harapan'])

    <main>
        <x-alert type="success" class="mb-4">
            Data siswa berhasil diperbarui.
        </x-alert>

        <x-alert>Jadwal semester baru sudah tersedia.</x-alert>
    </main>

    @include('partials.footer', ['namaSekolah' => 'SMP Harapan'])
</body>
</html>

Langkah 5 — Tambahkan route dan uji

routes/web.php
Route::get('/sekolah', fn () => view('sekolah'));

Jalankan php artisan serve, buka /sekolah, lalu cek dua hal: kedua alert memiliki markup dasar yang sama meski tipenya berbeda, dan header/footer dapat diubah dari file partial tanpa menyentuh halaman.

✅ Eksperimen kecilTambahkan tipe warning, lalu kirim atribut id="pengumuman" ke <x-alert>. Periksa melalui DevTools bahwa atribut tersebut diteruskan oleh $attributes.

Tugas / PR — Uji Pemahaman Hari Ini

Studi kasus: sekolah membutuhkan card siswa berisi foto, nama, dan kelas. Card yang sama harus dapat dipakai berulang pada halaman daftar siswa dan halaman detail kelas. Kerjakan bertingkat; jangan menyalin markup card ke setiap halaman.

Soal A — Dasar: buat kontrak card siswa

Buat anonymous component <x-card.siswa> yang menerima data foto, nama, dan kelas melalui props. Panggil minimal tiga kali dengan data berbeda di halaman daftar siswa.

Kriteria selesai:

  • File berada di folder component yang rapi dan nama tag cocok dengan lokasinya.
  • Semua foto memiliki alt bermakna; nama serta kelas berasal dari props.
  • Tidak ada tiga salinan struktur HTML card di halaman daftar.
💡 HintMulai dari @props(['foto', 'nama', 'kelas']).

Soal B — Menengah: gunakan dari dua konteks

Pakai component yang sama pada halaman daftar siswa dan detail kelas. Tambahkan variasi opsional untuk mode ringkas atau badge status tanpa membuat file card kedua.

Kriteria selesai:

  • Kedua halaman memanggil tag component yang sama.
  • Variasi memiliki default sehingga pemanggilan lama tetap bekerja.
  • Data dinamis dikirim dengan sintaks :, bukan menjadi string literal.
  • Tampilan standar dan variasi ringkas/status dapat dibedakan dengan jelas.
💡 HintPilih satu prop default seperti 'compact' => false atau satu named slot.

Soal C — Tantangan terbuka: aksi yang fleksibel

Tambahkan area aksi opsional. Di daftar siswa area ini berisi tautan “Lihat”, sedangkan di detail kelas dapat berisi “Pindahkan kelas”; card tetap rapi jika area aksi tidak diberikan. Putuskan apakah kebutuhan ini lebih cocok menjadi named slot atau prop, lalu tulis alasanmu dalam 2–3 kalimat di catatan PR.

Kriteria selesai:

  • Area aksi menerima HTML yang berbeda tanpa duplikasi component.
  • Card tanpa aksi tidak menghasilkan footer kosong.
  • Fokus keyboard, teks link/tombol, dan struktur HTML tetap masuk akal.
  • PR menyertakan tangkapan layar dua halaman serta penjelasan pilihan props/slot.
💡 HintNamed slot dapat diperiksa dengan @isset($actions).
⚠️ Batas tugasJangan mencari atau menempel solusi lengkap. Yang dinilai adalah kemampuan menentukan kontrak component, menggunakannya berulang, dan menjelaskan keputusan desain.

Playground PHP — Simulasi Component

Blade Component membutuhkan Laravel, sedangkan playground ini menjalankan PHP murni di browser memakai WebAssembly (php-wasm). Fungsi PHP di bawah mensimulasikan ide component: menerima “props”, memberi nilai default, mengamankan data, lalu menghasilkan tampilan yang konsisten.

🐘 PHP PlaygroundMemuat mesin PHP…
Hasil
Mesin PHP sedang disiapkan…
Tip: tekan Ctrl + Enter untuk menjalankan. Proses pertama mungkin butuh beberapa detik.
🔄 Hubungannya dengan BladeParameter array pada fungsi berperan seperti props, nilai ?? seperti default pada @props, dan argumen konten seperti slot. Di Laravel, Blade menangani rendering component dengan sintaks yang lebih nyaman.

Latihan Mandiri & Checklist Selesai

Latihan mandiri

  1. Di playground, tambahkan siswa ketiga lalu beri status berbeda. Pastikan format card tetap konsisten.
  2. Pilih contoh prop default dan ubah nilai defaultnya; amati pemanggilan yang tidak mengirim prop tersebut.
  3. Di proyek Laravel, pindahkan satu pola HTML berulang menjadi anonymous component.
  4. Tambahkan satu named slot opsional dan pastikan component tetap benar ketika slot tidak diisi.
  5. Susun component ke subfolder berdasarkan keluarga UI, lalu sesuaikan tag pemanggilannya.

Checklist sebelum lanjut Day 4

  • Bisa menjelaskan perbedaan HTML biasa, @include, anonymous component, dan class-based component.
  • Bisa membuat serta memanggil component dengan nama folder/tag yang benar.
  • Bisa mengirim prop statis dan dinamis, termasuk memberi nilai default.
  • Bisa memakai default slot dan named slot tanpa menduplikasi markup.
  • Bisa menelusuri data kosong dari view pemanggil sampai view component.
  • Component proyek tersusun rapi dan tidak dibuat untuk HTML sekali pakai.

🏁 Milestone Day 3 Tercapai!

Hari ini kamu sudah memahami Blade Component, props, slot, partial, serta cara memilih dan mengorganisir kode reusable. Kamu sekarang mampu membuat UI konsisten tanpa menyalin markup ke banyak halaman.