Belajar Livewire 4 #10 – Create Page Component

Tutorial lengkap Livewire 4 dan Laravel 12 yang membahas konsep, fitur terbaru, dan praktik terbaik membangun aplikasi web modern tanpa ribet JavaScript. Materi disusun bertahap dari dasar hingga studi kasus nyata dengan penjelasan mengalir dan mudah dipahami.

✅ Telah dilihat 144 kali

Rating: 5.00 ⭐

... 04 February 2026, 06:28

Pada sesi kali ini, kita akan membuat page component yang berfungsi untuk menangani logika penyimpanan data (create post) menggunakan Livewire 4 Single File Component.

Single File Component di Livewire 4 memungkinkan kita menuliskan class component dan Blade view dalam satu file, sehingga kode menjadi lebih ringkas, mudah dibaca, dan terorganisir.

Pastikan teman-teman masih berada di direktori root project Laravel, lalu jalankan perintah berikut:

php artisan make:livewire pages::post.create

Penjelasan Perintah

Perintah di atas memiliki arti sebagai berikut:

  • make:livewire Digunakan untuk membuat komponen Livewire baru
  • pages::post.create Menandakan bahwa:
    • Komponen berada di folder pages/post
    • Nama file component adalah create
    • Komponen ini akan difungsikan sebagai halaman (page component)

Dengan struktur tersebut, Livewire 4 akan otomatis membuat single file component.


Output yang Dihasilkan

Jika perintah berhasil dijalankan, maka akan muncul pesan seperti berikut:

INFO  Livewire component 
[C:\Users\LagiKoding\Desktop\simple-livewire4\resources\views/pages/post/⚡create.blade.php] 
created successfully.

Apa Artinya?

Pesan ini menandakan bahwa:

  • File component berhasil dibuat

  • Lokasi file berada di:

    resources/views/pages/post/⚡create.blade.php
    

Konfigurasi File create.blade.php

Silakan teman-teman buka file resources/views/pages/post/⚡create.blade.php kemudian ubah menjadi berikut ini:

<?php

use App\Models\Post;
use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\Attributes\Validate;

new class extends Component
{
    use WithFileUploads;

    #[Validate('required|image|max:2048')]
    public $image;

    #[Validate('required|min:3')]
    public $title = '';

    #[Validate('required')]
    public $content = '';

    public function store()
    {
        $this->validate();

        $imagePath = $this->image->store('posts', 'public');

        Post::create([
            'image'   => basename($imagePath),
            'title'   => $this->title,
            'content' => $this->content,
        ]);

        session()->flash('message', 'Post berhasil disimpan.');

        return $this->redirect('/posts', navigate: true);
    }

    public function render()
    {
        return $this->view()
            ->layout('layouts::app')
            ->title('Create Post');
    }
};
?>

<div class="max-w-3xl mx-auto space-y-6">

    {{-- Header --}}
    <div>
        <h1 class="text-2xl font-semibold tracking-tight text-gray-900">
            Create Post
        </h1>
        <p class="text-sm text-gray-600">
            Tambahkan post baru dengan tampilan modern.
        </p>
    </div>

    {{-- Flash Message --}}
    @if (session()->has('message'))
        <div class="bg-green-500/10 border border-green-500/20
                    text-green-700 px-4 py-3 rounded-xl backdrop-blur">
            {{ session('message') }}
        </div>
    @endif

    {{-- Form Card --}}
    <div class="rounded-2xl bg-white/70 backdrop-blur-xl
                border border-white/30 shadow-xl p-6 md:p-8 space-y-6">

        <form wire:submit.prevent="store" class="space-y-6">

            {{-- Image --}}
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">
                    Featured Image
                </label>

                <input
                    type="file"
                    wire:model="image"
                    class="block w-full text-sm text-gray-700
                           file:mr-4 file:py-2 file:px-4
                           file:rounded-xl file:border-0
                           file:bg-indigo-500/10 file:text-indigo-700
                           hover:file:bg-indigo-500/20"
                >

                @error('image')
                    <p class="text-sm text-red-600 mt-1">{{ $message }}</p>
                @enderror

                {{-- Preview --}}
                @if ($image)
                    <div class="mt-4">
                        <img
                            src="{{ $image->temporaryUrl() }}"
                            class="w-48 h-32 object-cover rounded-xl shadow"
                        >
                    </div>
                @endif
            </div>

            {{-- Title --}}
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">
                    Title
                </label>

                <input
                    type="text"
                    wire:model.defer="title"
                    placeholder="Judul post..."
                    class="w-full rounded-xl border border-white/40 bg-white/60
                           px-4 py-2 text-gray-900
                           focus:outline-none focus:ring-2 focus:ring-indigo-500/40"
                >

                @error('title')
                    <p class="text-sm text-red-600 mt-1">{{ $message }}</p>
                @enderror
            </div>

            {{-- Content --}}
            <div>
                <label class="block text-sm font-medium text-gray-700 mb-1">
                    Content
                </label>

                <textarea
                    wire:model.defer="content"
                    rows="6"
                    placeholder="Tulis konten post..."
                    class="w-full rounded-xl border border-white/40 bg-white/60
                           px-4 py-2 text-gray-900
                           focus:outline-none focus:ring-2 focus:ring-indigo-500/40"
                ></textarea>

                @error('content')
                    <p class="text-sm text-red-600 mt-1">{{ $message }}</p>
                @enderror
            </div>

            {{-- Actions --}}
            <div class="flex items-center gap-3 pt-2">
                <button
                    type="submit"
                    class="inline-flex items-center px-5 py-2.5 rounded-xl
                           bg-indigo-600 text-white text-sm font-medium
                           hover:bg-indigo-700 transition shadow">
                    Save Post
                </button>

                <a
                    href="/posts"
                    wire:navigate
                    class="inline-flex items-center px-5 py-2.5 rounded-xl
                           bg-gray-500/80 text-white text-sm font-medium
                           hover:bg-gray-600 transition shadow">
                    Back
                </a>
            </div>

        </form>
    </div>
</div>

1. Bagian Class Component (Logika)

Bagian paling atas file ini adalah class Livewire, yang bertugas menangani data, validasi, dan proses simpan ke database.

Import Class yang Digunakan

use App\Models\Post;
use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\Attributes\Validate;

Penjelasannya:

  • Post Digunakan untuk menyimpan data ke tabel posts
  • Component Class utama Livewire
  • WithFileUploads Wajib digunakan jika kita ingin upload file (image)
  • Validate Fitur validasi modern di Livewire 4 menggunakan attribute

Properti dan Validasi

#[Validate('required|image|max:2048')]
public $image;

Artinya:

  • required → image wajib diisi
  • image → harus berupa file gambar
  • max:2048 → maksimal ukuran 2MB

#[Validate('required|min:3')]
public $title = '';
  • Judul wajib diisi
  • Minimal 3 karakter

#[Validate('required')]
public $content = '';
  • Konten post juga wajib diisi

Dengan pendekatan ini, validasi menjadi lebih rapi karena langsung menempel pada properti.


2. Method store() (Proses Simpan Data)

public function store()
{
    $this->validate();

Saat form disubmit:

  • Livewire akan menjalankan validasi sesuai rule yang sudah kita definisikan

Upload Image ke Storage

$imagePath = $this->image->store('posts', 'public');

Artinya:

  • File disimpan ke folder storage/app/public/posts
  • Disk yang digunakan adalah public

Nantinya file ini bisa diakses melalui:

storage/posts/nama_file.jpg

Simpan ke Database

Post::create([
    'image'   => basename($imagePath),
    'title'   => $this->title,
    'content' => $this->content,
]);

Di sini kita menyimpan:

  • Nama file image saja (basename)
  • Title
  • Content

Kenapa hanya nama file? Supaya path bisa kita atur lebih fleksibel di tampilan.


Flash Message & Redirect

session()->flash('message', 'Post berhasil disimpan.');

Digunakan untuk menampilkan notifikasi setelah data berhasil disimpan.

return $this->redirect('/posts', navigate: true);
  • Redirect ke halaman list post
  • navigate: true agar tetap menggunakan sistem navigasi Livewire (tanpa reload penuh)

3. Method render()

public function render()
{
    return $this->view()
        ->layout('layouts::app')
        ->title('Create Post');
}

Artinya:

  • Menggunakan layout app
  • Memberi judul halaman Create Post

4. Bagian Tampilan (Blade View)

Setelah penutup ?>, kita masuk ke tampilan HTML + Tailwind.


Header Halaman

<h1 class="text-2xl font-semibold tracking-tight text-gray-900">
    Create Post
</h1>

Menampilkan judul halaman dengan tampilan modern dan bersih.


Flash Message

@if (session()->has('message'))

Digunakan untuk menampilkan pesan sukses setelah data disimpan.


Form Livewire

<form wire:submit.prevent="store">

Artinya:

  • Saat form disubmit
  • Tidak reload halaman
  • Menjalankan method store() di Livewire

Input Image + Preview

<input type="file" wire:model="image">
  • wire:model="image" menghubungkan input dengan properti $image
@if ($image)
    <img src="{{ $image->temporaryUrl() }}">
@endif

Fitur keren Livewire:

  • Bisa preview gambar sebelum disimpan
  • Tidak perlu JavaScript tambahan

Input Title & Content

Menggunakan:

wire:model.defer

Artinya:

  • Nilai dikirim saat submit
  • Lebih hemat request dibanding wire:model biasa

Tombol Aksi

  • Save Post → submit form
  • Back → kembali ke halaman /posts menggunakan wire:navigate

Konfigurasi Rute

Langkah berikutnya adalah menghubungkan page component Livewire yang sudah kita buat ke dalam sistem routing Laravel.

Silakan buka file routes/web.php, lalu sesuaikan isinya menjadi seperti berikut:

<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});
Route::livewire('/posts', 'pages::post.index');
Route::livewire('/posts/create', 'pages::post.create');


Penjelasan

Route Create Post

Route::livewire('/posts/create', 'pages::post.create');

Artinya:

  • URL /posts/create

  • Akan menampilkan page component:

    resources/views/pages/post/⚡create.blade.php
    

Halaman ini berfungsi untuk:

  • Menampilkan form input post
  • Upload image
  • Validasi data
  • Menyimpan data ke database menggunakan Livewire

Semua logika dan tampilan ditangani oleh satu file, tanpa controller tambahan.

Jika kita akses maka akan terlihat seperti pada gambar berikut ini:

Silakan coba dengan input data apakah berhasil. Pada materi berikutnya, kita akan membuat sebuah page component lagi yang berfungsi untuk edit data.

Daftar eBook