Belajar Livewire - Rendering & Layout
Episode 8 of 23

Belajar Livewire - Rendering & Layout

Menyusun kerangka halaman Livewire: layout dengan komponen Blade <x-layout>, dynamic page titles via #[Title], serta memecah UI menjadi komponen modular seperti header, sidebar, dan content untuk aplikasi yang rapi dan mudah dipelihara.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Sejauh ini kita fokus pada komponen kecil yang mandiri. Di aplikasi nyata, komponen-komponen itu tinggal di dalam layout — kerangka halaman yang berisi header, sidebar, dan area konten. Sebuah aplikasi admin panel yang baik tidak dibangun sebagai satu komponen raksasa, melainkan sebagai modul yang masing-masing punya tanggung jawab jelas.

Di episode ini kita membahas cara menyusun kerangka tersebut: layout full-page untuk komponen Livewire, pengaturan judul halaman yang dinamis, dan pola modularisasi UI menjadi komponen-komponen kecil. Ini adalah keputusan arsitektur yang menentukan seberapa mudah aplikasi kalian tumbuh.

Layout Full-Page untuk Livewire

Saat komponen dirender sebagai full-page (melalui Route::get('/x', Component::class)), Livewire perlu tahu layout mana yang dipakai. Ada dua cara:

1. Attribute #[Layout]

Deklarasikan layout langsung di komponen:

app/Livewire/Dashboard.php
<?php
 
namespace App\Livewire;
 
use Livewire\Attributes\Layout;
use Livewire\Component;
 
#[Layout('components.layouts.app')]
class Dashboard extends Component
{
    public function render()
    {
        return view('livewire.dashboard');
    }
}

2. Layout dinamis dari route

Jika layout perlu berbeda per route, atur lewat method layout:

routes/web.php
use App\Livewire\Dashboard;
 
Route::get('/dashboard', Dashboard::class)->layout('components.layouts.app');

Aturan default Livewire: jika tidak ada #[Layout], ia mencari components.layouts.app. Untuk satu aplikasi, biasanya cukup satu layout utama — pilih cara eksplisit (#[Layout]) agar jelas.

Komponen Layout dengan Slots

Layout adalah komponen Blade biasa yang memakai slot untuk area konten:

resources/views/components/layouts/app.blade.php
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ $title ?? config('app.name') }}</title>
    @vite(['resources/css/app.css'])
    @livewireStyles
</head>
<body>
    <x-layout.header />
 
    <div class="flex">
        <x-layout.sidebar />
        <main class="flex-1 p-6">
            {{ $slot }}
        </main>
    </div>
 
    <x-layout.footer />
 
    @livewireScripts
</body>
</html>

Slot {{ $slot }} adalah tempat konten halaman disuntikkan. Bagian lain — header, sidebar, footer — dipecah menjadi komponen Blade kecil (<x-layout.header />, <x-layout.sidebar />) agar layout tetap pendek dan mudah dibaca.

Dynamic Page Title

Setiap halaman perlu judul yang benar — bukan hanya untuk UX, tapi untuk SEO dan bookmarking. Livewire menyediakan attribute #[Title]:

Judul halaman statis
<?php
 
namespace App\Livewire;
 
use Livewire\Attributes\Title;
use Livewire\Component;
 
#[Title('Dashboard')]
class Dashboard extends Component {}

Untuk judul dinamis yang bergantung pada state (misalnya judul post yang sedang diedit), gunakan method title:

Judul halaman dinamis
public function title()
{
    return "Edit: {$this->post->title}";
}

Livewire akan memakai hasil method title() sebagai <title> halaman, dan memperbaruinya saat state berubah — berguna saat kalian mengganti data dari dalam komponen tanpa reload.

Tip

Untuk full-page components, jangan menaruh <title> statis di layout utama jika halaman punya judul sendiri — biarkan Livewire mengisinya lewat #[Title]/method title(). Judul yang salah sering kali tidak terdeteksi sampai aplikasi online dan user menyimpan halaman tersebut.

Modularisasi UI: Header, Sidebar, Content

Kunci aplikasi yang sehat adalah memecah UI menjadi komponen dengan satu tanggung jawab. Contoh nyata untuk dashboard:

resources/views/components/layout/header.blade.php
<header class="bg-white shadow">
    <nav>
        <x-layout.nav-link href="{{ route('dashboard') }}">Dashboard</x-layout.nav-link>
        <x-layout.nav-link href="{{ route('posts.index') }}">Posts</x-layout.nav-link>
    </nav>
</header>

Prinsip yang sama berlaku di dalam halaman. Daripada satu komponen PostDashboard yang mengerjakan segalanya, pecah menjadi komponen-komponen fungsional:

resources/views/livewire/posts/dashboard.blade.php
<div>
    <x-badge :count="$posts->count()" label="Total post" />
 
    <livewire:posts-table :posts="$posts" />
 
    <livewire:posts-stats />
</div>

Setiap blok punya logika dan state sendiri:

  • Badge — komponen presentasional, tidak punya state.
  • PostsTable — komponen Livewire yang menangani sorting/pagination.
  • PostsStats — komponen Livewire yang menangani event (seperti di episode 7).

Hasilnya: perubahan pada satu modul tidak merambat ke modul lain, dan setiap modul bisa diuji mandiri.

Pitfall Umum

  • Layout tidak ditemukan: pastikan nama layout sesuai path aktual (components.layouts.appresources/views/components/layouts/app.blade.php).
  • @livewireScripts hilang dari layout: interaksi mati (sudah dibahas episode 3 — tetap jadi penyebab nomor satu).
  • Judul statis di layout menimpa #[Title]: pilih salah satu sumber judul untuk konsistensi.
  • Satu komponen raksasa untuk seluruh halaman: sulit diuji, payload membengkak, morphing melambat. Pecah sejak awal.

Penutup

Inti yang harus dibawa pulang:

  • Full-page component memakai #[Layout] atau layout dari route; default components.layouts.app.
  • Layout memakai slot {{ $slot }}; pecah header/sidebar/footer menjadi komponen Blade kecil.
  • #[Title('...')] untuk judul statis, method title() untuk judul dinamis.
  • Modularisasi: pecah dashboard menjadi komponen presentasional (badge) dan komponen berstate (table, stats).

Di episode 9 selanjutnya kita akan membahas forms & validation — mengelola state form dengan property per field, validasi #[Validate]/validate(), validasi real-time dengan validateOnly(), custom error messages, dan multi-step forms. Ini akan menjadi inti dari hampir semua aplikasi yang kalian bangun. Sampai jumpa di episode 9!