@include memang bisa membagikan potongan tampilan, tetapi API-nya longgar: data dikirim lewat array, tidak ada deklarasi properti, dan nama variabel bisa bertabrakan tanpa terdeteksi. Component memperbaiki semuanya — potongan tampilan mendapat tag HTML sendiri seperti <x-alert>, properti yang jelas, serta tempat logika bila dibutuhkan. Laravel menyediakan dua rasa: anonymous component yang murni berkas Blade, dan class-based component dengan class PHP penuh. Kita bangun keduanya di chapter ini, lalu merendernya lewat HTTP untuk melihat HTML akhirnya; konsep ini adalah dasar dari component Blade
modern.
Anonymous Component: x-alert #
Anonymous component hanyalah berkas Blade di folder khusus resources/views/components/. Nama berkas menjadi nama tag: alert.blade.php otomatis tersedia sebagai <x-alert> di view mana pun. Buat berkasnya:
{{-- resources/views/components/alert.blade.php --}}
@props(['type' => 'info', 'title' => null])
<div class="alert alert-{{ $type }}">
@if ($title)
<strong>{{ $title }}:</strong>
@endif
{{ $slot }}
</div>Directive @props mendeklarasikan properti component — daftar nama yang boleh diterima selain atribut biasa. Nilai setelah => adalah default: type ber-fallback info bila tidak dikirim, dan title bernilai null sehingga blok @if di bawahnya otomatis melewati heading. Isi di antara tag pembuka dan penutup <x-alert> tersedia sebagai variabel $slot — kita bedah tuntas di chapter Slots dan Attributes.
Prop Statis dan Dinamis #
Component dipanggil seperti tag HTML biasa. Atribut statis dikirim sebagai string; atribut berawalan titik dua adalah ekspresi PHP yang dievaluasi lebih dulu. Halaman resources/views/contoh-component.blade.php memakai keduanya:
<h2>Pengaturan Akun</h2>
<x-alert type="success" title="Berhasil">
Profil Anda sudah diperbarui.
</x-alert>
<x-alert :type="$status">
Perubahan baru aktif setelah sesi berikutnya.
</x-alert>Route mengirim ['status' => 'warning'], sehingga :type="$status" menghasilkan prop $type = 'warning'. Halaman dirender lewat route biasa dan diambil dengan GET /pengaturan; Output yang Dihasilkan:
<div class="alert alert-success">
<strong>Berhasil:</strong>
Profil Anda sudah diperbarui.
</div>
<div class="alert alert-warning">
Perubahan baru aktif setelah sesi berikutnya.
</div>Tanpa atribut apa pun, default dari @props bekerja — <x-alert>Tanpa prop.</x-alert> menghasilkan class="alert alert-info" karena prop $type jatuh ke nilai bawaannya.
Class-based Component: x-button #
Saat component butuh logika lebih dari sekadar default, pindahkan kode ke class PHP. Perintah make:component menyiapkan pasangan class dan view:
$ php artisan make:component Button
INFO Component [app/View/Components/Button.php] created successfully.
INFO View [resources/views/components/button.blade.php] created successfully.Properti dideklarasikan lewat constructor — dengan type declaration penuh karena ini class PHP biasa. Method pada class juga bisa dipanggil langsung dari view-nya. Isi app/View/Components/Button.php:
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class Button extends Component
{
public function __construct(
public string $label = 'Simpan',
public string $variant = 'primary',
) {}
public function variantClass(): string
{
return match ($this->variant) {
'primary' => 'btn btn-primary',
'secondary' => 'btn btn-outline',
'danger' => 'btn btn-danger',
default => 'btn',
};
}
public function render(): View|Closure|string
{
return view('components.button');
}
}Sedangkan resources/views/components/button.blade.php tinggal memakai properti dan method tersebut:
<button type="button" class="{{ $variantClass() }}">{{ $label }}</button>Pemetaan match pada variantClass() menunjukkan letak kekuatan class-based component: logika non-trivial — pemetaan variant, perhitungan tanggal, query ringan — hidup di class yang bisa diuji terpisah, bukan tercecer di template.
Merender Semuanya dalam Satu Halaman #
Kedua jenis component hidup berdampingan di satu halaman yang sama:
<x-button label="Simpan Profil" />
<x-button label="Hapus Akun" variant="danger" />Output yang Dihasilkan untuk GET /pengaturan (heading dan kedua alert memakai contoh sebelumnya):
<button type="button" class="btn btn-primary">Simpan Profil</button>
<button type="button" class="btn btn-danger">Hapus Akun</button>Tag <x-button /> yang ditulis menutup sendiri menandakan component tanpa slot; bila ada konten, tag pembuka dan penutup wajib berpasangan. Nama prop yang terdiri dari dua kata pun punya kesepakatan sendiri: di tag ditulis kebab-case, di @props dan constructor ditulis camelCase. Prop nilaiDefault misalnya dikirim lewat atribut nilai-default="Nilai awal" — kita verifikasi pemetaan ini menghasilkan Nilai awal tepat di dalam prop, sementara tag tanpa atribut tersebut jatuh kembali ke nilai default-nya. Pilihan anonymous atau class-based pun fleksibel: mulai dari anonymous, lalu angkat ke class saat logikanya tumbuh — perintah make:component Alert --view bahkan bisa membuat anonymous component dari terminal.
Tag, properti, dan isi sudah berjalan. Yang belum kita sentuh adalah dua objek istimewa di baliknya: $slot penampung konten dan $attributes penampung atribut — keduanya menjadi fokus chapter berikutnya.