Tutorial Laravel 12 Inertia React JS dan Tailwind CSS #8 Membuat Halaman Index

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

Rating: 5.00 ⭐

... 30 July 2025, 10:00

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

Setelah sebelumnya kita berhasil membuat controller, menambahkan rute, dan juga menginstal beberapa komponen dari shadcn/ui, sekarang kita akan masuk ke tahap berikutnya, yaitu membuat halaman index. Halaman ini nantinya berfungsi untuk menampilkan daftar data produk yang ada di dalam aplikasi.

Struktur Folder

Sebelum kita mulai ngoding, pastikan kita menyiapkan struktur folder yang rapi agar proyek kita lebih mudah dipelihara dalam jangka panjang.

Langkah pertama, buat folder baru dengan nama Products di dalam direktori resources/js/pages. Folder ini akan menjadi tempat untuk semua halaman yang berkaitan dengan produk, seperti halaman index, create, edit, dan lain sebagainya.

Setelah itu, di dalam folder Products yang baru saja dibuat, buat satu file baru dengan nama Index.tsx. File ini akan berisi komponen React untuk menampilkan daftar produk.

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

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

Struktur ini umum digunakan dalam proyek Laravel + Inertia + React, supaya kita bisa dengan mudah mengorganisir setiap halaman berdasarkan fiturnya. Silakan buka file Index.tsx kemudian masukan kode berikut ini:

import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Button } from '@/components/ui/button';
import AppLayout from '@/layouts/app-layout';
import { type BreadcrumbItem } from '@/types';
import { Head, Link, useForm, usePage } from '@inertiajs/react';
import { Megaphone } from 'lucide-react';
import {
    Table,
    TableBody,
    TableCaption,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from "@/components/ui/table"

const breadcrumbs: BreadcrumbItem[] = [
    {
        title: 'Products',
        href: '/products',
    },
];

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

interface PageProps {
    flash: {
        message?: string
    },
    products: Product[]
}

export default function Index() {

    const { products, flash } = usePage().props as PageProps;

    const {processing, delete: destroy} = useForm();

    const handleDelete = (id: number, name: string) => {
        if(confirm(`Do you want to delete a product - ${id}. ${name}`)){
            destroy(route("products.destroy", id));
        }
    }

    return (
        <AppLayout breadcrumbs={breadcrumbs}>
            <Head title="Products" />
            <div className='m-4'>
                <Link href={route('products.create')}><Button>Tambah Produk</Button></Link>
            </div>
            <div className='m-4'>
                <div>
                    {flash.message && (
                        <Alert>
                            <Megaphone className="h-4 w-4" />
                            <AlertTitle>Notification!</AlertTitle>
                            <AlertDescription>
                                {flash.message}
                            </AlertDescription>
                        </Alert>
                    )}
                </div>
            </div>
            {products.length > 0 && (
                <div className='m-4'>
                    <Table>
                        <TableCaption>Daftar Produk.</TableCaption>
                        <TableHeader>
                            <TableRow>
                                <TableHead className="w-[100px]">ID</TableHead>
                                <TableHead>Nama</TableHead>
                                <TableHead>Harga</TableHead>
                                <TableHead>Deskripsi</TableHead>
                                <TableHead className="text-center">Aksi</TableHead>
                            </TableRow>
                        </TableHeader>
                        <TableBody>
                            {products.map((product) => (
                                <TableRow>
                                    <TableCell className="font-medium">{product.id}</TableCell>
                                    <TableCell>{product.name}</TableCell>
                                    <TableCell>{product.price}</TableCell>
                                    <TableCell>{product.description}</TableCell>
                                    <TableCell className="text-center space-x-2">
                                        <Link href={route('products.edit', product.id)}><Button className="bg-slate-600 hover:bg-slate-700">Edit</Button></Link>
                                        <Button disabled={processing} onClick={() => handleDelete(product.id, product.name)} className="bg-red-500 hover:bg-red-700">Delete</Button>
                                    </TableCell>
                                </TableRow>
                            ))}
                           
                        </TableBody>
                    </Table>

                </div>

            )}
        </AppLayout>
    );
}

Bagian Import

import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Button } from '@/components/ui/button';
import AppLayout from '@/layouts/app-layout';
import { type BreadcrumbItem } from '@/types';
import { Head, Link, useForm, usePage } from '@inertiajs/react';
import { Megaphone } from 'lucide-react';
import {
    Table,
    TableBody,
    TableCaption,
    TableCell,
    TableHead,
    TableHeader,
    TableRow,
} from "@/components/ui/table"

Penjelasan:

  • AlertButtonTable, dsb. → komponen dari shadcn/ui. Ini semacam UI Kit modern berbasis Tailwind.
  • AppLayout → layout dasar untuk menyatukan tampilan (header, breadcrumb, dll).
  • useFormusePage dari @inertiajs/react → untuk handle form dan akses props dari Laravel.
  • Megaphone → icon dari lucide-react buat hiasan alert.
  • Head → set judul halaman (meta <title>).
  • Link → navigasi tanpa reload (SPA style ala Inertia).
  • BreadcrumbItem → type data untuk breadcrumb navigasi.

Data Awal dan Interface

const breadcrumbs: BreadcrumbItem[] = [
    {
        title: 'Products',
        href: '/products',
    },
];
  • Breadcrumb ini akan dikirim ke AppLayout untuk ditampilkan di atas halaman.
interface Product {
    id: number,
    name: string,
    price: number,
    description: string,
}

interface PageProps {
    flash: {
        message?: string
    },
    products: Product[]
}
  • Type Product → merepresentasikan struktur data produk.
  • Type PageProps → ini props yang dikirim dari controller Laravel ke React (via Inertia). Ada:
    • flash.message → pesan sukses / notifikasi.
    • products → array daftar produk.

Komponen Utama: Index

export default function Index() {
    const { products, flash } = usePage().props as PageProps;
    const {processing, delete: destroy} = useForm();
  • usePage().props → mengambil data dari controller Laravel.
  • useForm() → membuat form helper ala Inertia. Kita ambil method delete dan rename jadi destroy.

Fungsi Hapus

    const handleDelete = (id: number, name: string) => {
        if(confirm(`Do you want to delete a product - ${id}. ${name}`)){
            destroy(route("products.destroy", id));
        }
    }
  • Fungsi handleDelete ini akan:
    1. Munculkan konfirmasi confirm(...)
    2. Kalau user klik “OK”, maka jalankan request DELETE ke route products.destroy dengan ID produk.

Tampilan Layout

return (
    <AppLayout breadcrumbs={breadcrumbs}>
  • Semua isi halaman ini akan dibungkus dalam layout AppLayout, dan breadcrumb dikirim ke sana.

Bagian Head + Tombol Tambah

<Head title="Products" />
<div className='m-4'>
    <Link href={route('products.create')}><Button>Tambah Produk</Button></Link>
</div>
  • <Head /> → set judul tab browser jadi "Products".
  • Tombol "Tambah Produk" → link ke route products.create.

Flash Message

{flash.message && (
    <Alert>
        <Megaphone className="h-4 w-4" />
        <AlertTitle>Notification!</AlertTitle>
        <AlertDescription>
            {flash.message}
        </AlertDescription>
    </Alert>
)}
  • Kalau ada flash.message, maka tampilkan alert (dari shadcn/ui) dengan icon Megaphone.

Tabel Produk

{products.length > 0 && (
    <Table>
        ...
    </Table>
)}

Tabel Header:

<TableHeader>
    <TableRow>
        <TableHead className="w-[100px]">ID</TableHead>
        <TableHead>Nama</TableHead>
        <TableHead>Harga</TableHead>
        <TableHead>Deskripsi</TableHead>
        <TableHead className="text-center">Aksi</TableHead>
    </TableRow>
</TableHeader>

Tabel Body:

<TableBody>
    {products.map((product) => (
        <TableRow>
            <TableCell className="font-medium">{product.id}</TableCell>
            <TableCell>{product.name}</TableCell>
            <TableCell>{product.price}</TableCell>
            <TableCell>{product.description}</TableCell>
            <TableCell className="text-center space-x-2">
                <Link href={route('products.edit', product.id)}><Button>Edit</Button></Link>
                <Button disabled={processing} onClick={() => handleDelete(product.id, product.name)} className="bg-red-500 hover:bg-red-700">Delete</Button>
            </TableCell>
        </TableRow>
    ))}
</TableBody>
  • Setiap baris menampilkan info produk.
  • Kolom terakhir adalah tombol aksi: edit dan hapus.
  • Tombol delete memanggil handleDelete().

Run Project

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

composer run dev

Pastikan service apache dan mysql sudah berjalan dan silakan mendaftar untuk membuat akun baru dihalaman http://127.0.0.1:8000/register

Maka, jika kita akses rute:

http://127.0.0.1:8000/products/

Akan tampil seperti pada gambar berikut ini:


Kesimpulan

Jadi halaman ini bertujuan untuk:

  • Menampilkan daftar produk.
  • Menyediakan navigasi untuk menambah/edit produk.
  • Menampilkan notifikasi ketika ada flash message.
  • Bisa menghapus data produk dengan konfirmasi.

Kalau analoginya kita bawa ke dunia nyata:

Ini ibarat halaman "Daftar Produk" di dashboard admin toko online. Admin bisa melihat produk yang tersedia, menambahkan produk baru, mengedit yang lama, atau menghapus kalau perlu. Notifikasinya muncul kalau barusan selesai operasi tertentu, misal habis tambah produk sukses, maka muncul notifikasi “Produk berhasil ditambahkan”.

Pada materi selanjutnya, kita akan membuat halaman untuk proses input/create data.

Daftar eBook