Tutorial Laravel 12 Inertia React JS dan Tailwind CSS #10 Membuat Halaman Edit

Belajar membangun aplikasi web modern menggunakan Laravel 12 sebagai backend, dengan Inertia.js sebagai jembatan ke React JS, dan styling menggunakan Tailwind CSS. Cocok untuk pemula yang ingin memahami integrasi Laravel API dengan frontend modern.

✅ Telah dilihat 482 kali

Rating: 5.00 ⭐

... 30 July 2025, 10:00

Membuat Halaman Edit pada Starter Kit Laravel 12 dengan Inertia, React JS, dan Tailwind CSS

img

Struktur Folder

Silakan buat satu file baru dengan nama Edit.tsx. File ini akan berisi komponen React untuk menyimpan data produk.

Kalau sudah, kira-kira struktur foldernya akan terlihat seperti ini:

resources/
└── js/
    └── pages/
        └── Products/
            └── Index.tsx
            └── Create.tsx
            └── Edit.tsx

Silakan buka file Edit.tsx yang baru saja dibuat kemudian ubah menjadi seperti berikut ini:

import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import AppLayout from '@/layouts/app-layout';
import { type BreadcrumbItem } from '@/types';
import { Head, useForm, Link } from '@inertiajs/react';
import { CircleAlert, ArrowLeft } from 'lucide-react';

interface Product {
    id: number;
    name: string;
    description: string;
    price: number;
}

interface Props {
    product: Product;
}

export default function Edit({ product }: Props) {
    const { data, setData, put, processing, errors } = useForm({
        name: product.name,
        price: product.price,
        description: product.description,
    });

    const handleUpdate = (e: React.FormEvent) => {
        e.preventDefault();
        put(route('products.update', product.id));
    };

    const breadcrumbs: BreadcrumbItem[] = [
        {
            title: 'Edit Produk',
            href: `/products/${product.id}/edit`,
        },
    ];

    return (
        <AppLayout breadcrumbs={breadcrumbs}>
            <Head title="Edit Produk" />

            <div className="max-w-3xl mx-auto mt-12 p-8 bg-white dark:bg-zinc-900 shadow-xl rounded-2xl border border-gray-200 dark:border-zinc-700">
                <div className="flex justify-between items-center mb-6">
                    <h2 className="text-2xl font-bold text-gray-800 dark:text-white">Edit Produk</h2>
                    <Link
                        href="/products"
                        className="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-200 hover:text-primary"
                    >
                        <ArrowLeft className="w-5 h-5 mr-2" />
                    </Link>

                </div>

                <form onSubmit={handleUpdate} className="space-y-6">
                    {Object.keys(errors).length > 0 && (
                        <Alert variant="destructive">
                            <CircleAlert className="h-5 w-5" />
                            <AlertTitle className="text-base">Terjadi Kesalahan</AlertTitle>
                            <AlertDescription>
                                <ul className="list-disc list-inside text-sm space-y-1 mt-2">
                                    {Object.entries(errors).map(([key, message]) => (
                                        <li key={key}>{message as string}</li>
                                    ))}
                                </ul>
                            </AlertDescription>
                        </Alert>
                    )}

                    <div className="grid md:grid-cols-2 gap-6">
                        <div className="space-y-2">
                            <Label htmlFor="name" className="text-base font-medium text-gray-800 dark:text-gray-200">
                                Nama Produk
                            </Label>
                            <Input
                                id="name"
                                placeholder="e.g. Elegant Coffee Mug"
                                value={data.name}
                                onChange={(e) => setData('name', e.target.value)}
                                className="bg-white dark:bg-zinc-800 text-gray-900 dark:text-white border-gray-300 dark:border-zinc-600 focus-visible:ring-primary"
                            />
                        </div>

                        <div className="space-y-2">
                            <Label htmlFor="price" className="text-base font-medium text-gray-800 dark:text-gray-200">
                                Harga (IDR)
                            </Label>
                            <Input
                                id="price"
                                placeholder="e.g. 75000"
                                value={data.price}
                                onChange={(e) => setData('price', e.target.value)}
                                className="bg-white dark:bg-zinc-800 text-gray-900 dark:text-white border-gray-300 dark:border-zinc-600 focus-visible:ring-primary"
                            />
                        </div>
                    </div>

                    <div className="space-y-2">
                        <Label htmlFor="description" className="text-base font-medium text-gray-800 dark:text-gray-200">
                            Deskripsi
                        </Label>
                        <Textarea
                            id="description"
                            placeholder="Masukkan deskripsi produk"
                            value={data.description}
                            onChange={(e) => setData('description', e.target.value)}
                            className="bg-white dark:bg-zinc-800 text-gray-900 dark:text-white border-gray-300 dark:border-zinc-600 min-h-[120px] focus-visible:ring-primary"
                        />
                    </div>

                    <div className="pt-4">
                        <Button type="submit" disabled={processing} className="w-full text-base py-6 rounded-xl">
                            {processing ? 'Updating...' : 'Perbarui Produk'}
                        </Button>
                    </div>
                </form>
            </div>
        </AppLayout>
    );
}

Bagian Import

import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';

Ini semua adalah komponen UI dari library ShadCN atau komponen kustom. Tujuannya untuk membuat form yang rapi dan konsisten.

import AppLayout from '@/layouts/app-layout';

Digunakan sebagai layout utama halaman. Biasanya ini sudah memuat navbar, sidebar, dan struktur HTML umum lainnya.

import { type BreadcrumbItem } from '@/types';

Digunakan untuk mendefinisikan breadcrumbs (navigasi jejak lokasi halaman).

import { Head, useForm, Link } from '@inertiajs/react';
  • Head → Untuk atur judul halaman browser.
  • useForm → Hook dari Inertia buat ngatur form (mirip useState tapi khusus form).
  • Link → Untuk navigasi antar halaman ala SPA (tanpa reload).
import { CircleAlert, ArrowLeft } from 'lucide-react';

Icon dari Lucide buat keperluan UI, misalnya icon notifikasi error dan tombol kembali.

Interface Props & Data

interface Product {
  id: number;
  name: string;
  description: string;
  price: number;
}

interface Props {
  product: Product;
}

Kita buat tipe data Product, lalu Props adalah props yang diterima oleh komponen ini, yaitu data product dari server.

Inisialisasi Form

const { data, setData, put, processing, errors } = useForm({
  name: product.name,
  price: product.price,
  description: product.description,
});
  • useForm membuat state form.
  • data adalah nilai input saat ini.
  • setData untuk update nilai.
  • put adalah method Inertia untuk request PUT (update).
  • processing untuk disable tombol saat loading.
  • errors berisi pesan validasi dari backend.

Fungsi Submit

const handleUpdate = (e: React.FormEvent) => {
  e.preventDefault();
  put(route('products.update', product.id));
};

Saat form disubmit, kita panggil put() ke route products.update, sambil bawa data yang udah diisi ulang.

Breadcrumbs

const breadcrumbs: BreadcrumbItem[] = [
  {
    title: 'Edit Produk',
    href: `/products/${product.id}/edit`,
  },
];

Untuk menampilkan navigasi lokasi halaman saat ini.

Tampilan Form

Di dalam <AppLayout> kita isi struktur halaman:

  • Judul halaman (h2)
  • Tombol kembali (Link)
  • Form input: nama, harga, deskripsi.
  • Validasi error (kalau ada)
  • Tombol submit

Semua input terhubung dengan data dari useForm, dan akan dikirim saat tombol ditekan.

Alur Lengkap Halaman Edit Produk

  1. Data produk lama dikirim dari server ke komponen ini.
  2. User melihat form yang sudah terisi (pre-filled).
  3. User mengubah nilai form.
  4. Klik Perbarui Produk → data dikirim pakai PUT ke server.
  5. Jika sukses → biasanya akan redirect ke halaman index + tampil flash message.

Run Project

Untuk uji coba, silakan teman-teman jalankan project dengan cara menjalankan perintah:

composer run dev

Maka, jika kita akses rute:

http://127.0.0.1:8000/products/edit

Akan tampil seperti pada gambar berikut ini:

img

img

img

KESIMPULAN

Halaman ini berfungsi sebagai form edit produk dengan fitur:

  • Pre-filled form dengan data lama
  • Validasi error dari backend
  • Tombol kembali ke halaman produk
  • UX yang smooth karena pakai Inertia (tanpa reload halaman)

Kalau diibaratkan:

Ini kayak admin toko buka halaman edit untuk memperbarui info produk. Misalnya mau ganti harga, koreksi deskripsi, atau sekadar typo di nama produk. Setelah diedit, tinggal klik update dan selesai.

Pada materi berikutnya, kita akan melakukakan konfigurasi sidebar. yakni untuk menampilkan menu products didalam sidebar yang sudah ada.

Daftar eBook