Blade Component: Komponen Reusable
Bikin komponen <x-alert> dan <x-button> yang bisa dipakai ulang.
Analogi: Cetakan Kue Kering
Bayangkan kamu jualan kue kering. Setiap kue butuh bentuk bintang yang sama persis. Kamu tidak mengukir satu per satu pakai pisau, kamu pakai cetakan: tekan, jadi. Kalau besok mau mengubah bentuk bintangnya sedikit lebih gemuk, kamu ubah cetakannya sekali, dan semua kue berikutnya otomatis ikut berubah.
Component Blade itu cetakannya. Potongan HTML yang dipakai berulang (alert, tombol, kartu produk) dibikin sekali sebagai component, lalu dipakai di mana-mana dengan tag seperti <x-alert>. Ubah component-nya sekali, semua halaman yang memakainya ikut berubah.
Kenapa Component Ada?
Tanpa component, HTML untuk sebuah alert sukses akan di-copy-paste ke 20 halaman berbeda. Suatu hari desainnya harus diganti, dan kamu harus mengedit 20 file satu per satu. Pasti ada yang kelewat, dan tampilannya jadi tidak konsisten.
Component menyelesaikan dua masalah sekaligus: tidak ada duplikasi (DRY: Don't Repeat Yourself) dan satu sumber kebenaran untuk tampilan tiap potongan UI.
Contoh 1: Anonymous Component (Tanpa Class PHP)
Untuk component sederhana yang cuma butuh HTML + sedikit logika tampilan, kamu tidak perlu class PHP. Cukup bikin file view di folder components:
File: resources/views/components/alert.blade.php
@props(['type' => 'info'])
<div class="alert alert-{{ $type }}" role="alert">
{{ $slot }}
</div>@props mendefinisikan atribut yang diterima component (dengan nilai default 'info'). $slot adalah "lubang" tempat isi yang ditulis di antara tag pembuka dan penutup akan masuk.
Pakai di view mana pun:
File: resources/views/dashboard.blade.php
<x-alert type="success">
Data berhasil disimpan!
</x-alert>
<x-alert type="danger">
Gagal menghapus data.
</x-alert>
{{-- tanpa atribut type, pakai default 'info' --}}
<x-alert>
Ini info biasa.
</x-alert>Nama file alert.blade.php otomatis menjadi tag <x-alert>. File user-card.blade.php menjadi <x-user-card> (kebab-case).
Contoh 2: Class Component (Dengan Logika PHP)
Kalau component butuh logika (misal menghitung sesuatu, mengambil data), buat dengan artisan:
php artisan make:component TombolIkutiIni membuat dua file: class app/View/Components/TombolIkuti.php dan view resources/views/components/tombol-ikuti.blade.php.
File: app/View/Components/TombolIkuti.php
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class TombolIkuti extends Component
{
public function __construct(
public int $userId,
public bool $sudahIkuti = false,
) {}
public function label(): string
{
return $this->sudahIkuti ? 'Berhenti Ikuti' : 'Ikuti';
}
public function render(): View|Closure|string
{
return view('components.tombol-ikuti');
}
}File: resources/views/components/tombol-ikuti.blade.php
<button
data-user="{{ $userId }}"
class="btn {{ $sudahIkuti ? 'btn-secondary' : 'btn-primary' }}"
>
{{ $label() }}
</button>Pemakaian:
<x-tombol-ikuti :user-id="$user->id" :sudah-ikuti="$user->sudahDiikuti()" />Perhatikan titik dua (:) di depan nama atribut: artinya nilainya adalah ekspresi PHP, bukan string mentah. Tanpa titik dua, user-id="$user->id" akan dikirim sebagai teks harfiah $user->id.
Kesalahan Umum Pemula
1. Salah: lupa @props di anonymous component
{{-- resources/views/components/alert.blade.php --}}
<div class="alert alert-{{ $type }}">
{{ $slot }}
</div>Tanpa @props(['type' => 'info']), variabel $type tidak dikenal dan Blade error Undefined variable $type. Atribut yang tidak didaftarkan di @props malah masuk ke $attributes.
Benar:
@props(['type' => 'info'])
<div class="alert alert-{{ $type }}">
{{ $slot }}
</div>2. Salah: atribut PHP tanpa titik dua
{{-- user-id dikirim sebagai STRING "$user->id", bukan angkanya --}}
<x-tombol-ikuti user-id="$user->id" />Benar:
<x-tombol-ikuti :user-id="$user->id" />Ini jebakan paling sering. Ingat: tanpa :, semua atribut dianggap string.
3. Salah: nama tag tidak cocok dengan nama file
File resources/views/components/userCard.blade.php (camelCase) tidak akan dikenali sebagai <x-user-card>. Laravel mengharapkan nama file kebab-case: user-card.blade.php.
Catatan teknis: Atribut yang tidak terdaftar di
@propsotomatis terkumpul di$attributesdan bisa di-render dengan{{ $attributes->merge(['class' => '...']) }}. Ini berguna untuk class CSS tambahan dari pemakai component tanpa harus mendaftarkan setiap atribut satu per satu.
Class CSS Tambahan dari Pemakai
Atribut yang tidak didaftarkan di @props tidak hilang, ia terkumpul di $attributes. Ini cara component menerima class tambahan:
File: resources/views/components/button.blade.php
@props(['type' => 'button'])
<button type="{{ $type }}" {{ $attributes->merge(['class' => 'btn']) }}>
{{ $slot }}
</button>Pemakaian:
<x-button class="btn-primary mt-4">
Simpan
</x-button>Hasil HTML-nya: <button type="button" class="btn btn-primary mt-4">Simpan</button>. Class bawaan btn digabung otomatis dengan class dari pemakai. Pola ini bikin component fleksibel tanpa harus mendaftarkan setiap atribut satu per satu.
Tantangan
Component Card
Buat anonymous component x-card yang menerima props title dan menampilkan $slot sebagai isi. Pakai 3 kali di satu halaman dengan judul dan isi berbeda.