Tutorial Laravel 12 Inertia React JS dan Tailwind CSS #9 Membuat Halaman Create

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 597 kali

Rating: 5.00 ⭐

... 30 July 2025, 10:00

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

Struktur Folder

Silakan buat satu file baru dengan nama Create.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

Silakan buka file Create.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';

const breadcrumbs: BreadcrumbItem[] = [
    {
        title: 'Tambah Produk Baru',
        href: '/products/create',
    },
];

export default function CreateProductForm() {
    const { data, setData, post, processing, errors } = useForm({
        name: '',
        price: '',
        description: '',
    });

    const handleSubmit = (e: React.FormEvent) => {
        e.preventDefault();
        post(route('products.store'));
    };

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

            <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">Tambah Produk Baru</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={handleSubmit} 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. eBook Laravel"
                                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="eBook Laravel React JS Starter Kit by Lagikoding"
                            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 ? 'Saving...' : 'Simpan'}
                        </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 adalah komponen UI yang kita ambil dari shadcn/ui, seperti:

  • Alert = Untuk menampilkan notifikasi error.
  • Button, Input, Label, Textarea = Komponen form.
import AppLayout from '@/layouts/app-layout';

Layout utama aplikasi agar halaman ini tetap konsisten dengan tampilan aplikasi secara keseluruhan.

import { Head, useForm, Link } from '@inertiajs/react';

Fitur dari Inertia.js:

  • Head = Untuk mengatur judul halaman.
  • useForm = Hook dari Inertia untuk mengelola form state, submit, dan error.
  • Link = Untuk navigasi antar halaman (SPA-style).
import { CircleAlert, ArrowLeft } from 'lucide-react';

Icon-icon dari Lucide untuk keperluan UI:

  • CircleAlert untuk ikon alert/error.
  • ArrowLeft untuk tombol kembali.

Breadcrumbs

const breadcrumbs: BreadcrumbItem[] = [
  {
    title: 'Tambah Produk Baru',
    href: '/products/create',
  },
];

Ini hanya array untuk menunjukkan posisi halaman saat ini di navigasi (breadcrumbs). Biasanya ditampilkan di bagian atas halaman agar user tahu sedang berada di mana.


useForm: Menyiapkan Data dan Validasi

const { data, setData, post, processing, errors } = useForm({
  name: '',
  price: '',
  description: '',
});

Dengan useForm, kita mendefinisikan state awal form. Kemudian kita bisa:

  • setData → mengubah nilai input ketika user mengetik.
  • post() → mengirim data ke backend via route Laravel (products.store).
  • processing → untuk disable tombol submit saat sedang proses submit.
  • errors → menangkap error validasi dari backend (kalau ada).

handleSubmit

const handleSubmit = (e: React.FormEvent) => {
  e.preventDefault(); // mencegah reload halaman
  post(route('products.store'));
};

Fungsi ini akan dipanggil saat tombol submit ditekan. Ia akan mengirim data ke backend lewat route Laravel products.store, tanpa perlu AJAX manual karena sudah dihandle oleh Inertia.js.


Tampilan UI

<AppLayout breadcrumbs={breadcrumbs}>
  <Head title="Create a New Product" />

Ini bagian luar dari halaman, menggunakan layout standar dan mengatur judul tab browser.


Menampilkan Error Validasi

{Object.keys(errors).length > 0 && (
  <Alert variant="destructive">
    <CircleAlert />
    <AlertTitle>Terjadi Kesalahan</AlertTitle>
    <AlertDescription>
      <ul>
        {Object.entries(errors).map(([key, message]) => (
          <li key={key}>{message}</li>
        ))}
      </ul>
    </AlertDescription>
  </Alert>
)}

Kalau ada validasi error dari backend (misalnya field kosong, atau harga bukan angka), error akan ditampilkan dalam bentuk alert. Ini user-friendly dan membantu pengguna tahu apa yang salah.


Input Data Produk

<Input
  id="name"
  value={data.name}
  onChange={(e) => setData('name', e.target.value)}
/>

Ini contoh input field untuk name. Value-nya terikat ke data.name, dan ketika user mengetik, akan diupdate lewat setData.

Begitu juga dengan price dan description.


Tombol Submit

<Button type="submit" disabled={processing}>
  {processing ? 'Saving...' : 'Simpan'}
</Button>

Ini tombol submit untuk menyimpan data. Selama proses submit, tombol akan disable dan menampilkan teks Saving... agar user tahu sedang diproses.


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/create

Akan tampil seperti pada gambar berikut ini:


Kesimpulan

Jadi, halaman ini bertujuan untuk:

  • Menampilkan form input untuk menambahkan produk baru.
  • Mengirim data ke server menggunakan Inertia dan Laravel.
  • Menampilkan validasi jika ada kesalahan input dari user.
  • Mengarahkan kembali ke halaman daftar produk setelah proses tambah berhasil.
  • Menampilkan notifikasi sukses setelah data berhasil ditambahkan.

Kalau dianalogikan ke dunia nyata:

Halaman ini seperti saat admin toko online ingin menambahkan produk baru ke etalase tokonya. Admin mengisi form: nama produk, harga, dan keterangan lainnya. Setelah itu tinggal klik tombol simpan. Kalau semua input valid, maka produk langsung tersimpan dan muncul notifikasi “Produk berhasil ditambahkan.”

Di materi selanjutnya, kita akan masuk ke halaman edit, untuk mengubah data produk yang sudah ada sebelumnya.

Daftar eBook