September 27, 2026

Laravel + React.

Membangun aplikasi Laravel dengan frontend React memakai starter kit resmi: Vite, TypeScript, struktur halaman Inertia + React, autentikasi Fortify, dan langkah build untuk deployment.

Ada dua cara umum memadukan Laravel dengan React:

PendekatanCara kerjaCocok untuk
Monolith InertiaLaravel mengurus routing & data, React merender halaman (lihat Inertia.js )Aplikasi web tim kecil, dashboard, e-commerce
API + SPA terpisahLaravel sebagai REST/JSON API (Sanctum), React berdiri sendiri (Vite/Next.js )Banyak klien (web + mobile), tim frontend terpisah

Catatan ini fokus ke pendekatan pertama, karena itulah yang dipakai starter kit React resmi Laravel.


1. Membuat Project dari Starter Kit

Syarat: PHP 8.3+ (Laravel 13), Composer, Node.js LTS.

composer global require laravel/installer
laravel new my-app
# pilih: React β†’ autentikasi Laravel (atau WorkOS) β†’ testing (Pest/PHPUnit) β†’ database
cd my-app
npm install && npm run build
composer run dev

composer run dev menjalankan beberapa proses sekaligus: php artisan serve, Vite dev server (HMR), queue listener, dan log viewer. Buka http://localhost:8000.

Stack yang didapat:

LapisanTeknologi
BackendLaravel 13, Fortify (autentikasi)
JembatanInertia
FrontendReact 19 + TypeScript
Styling & UITailwind CSS 4 + shadcn/ui
BundlerVite + laravel-vite-plugin
Routing type-safeWayfinder (route Laravel β†’ fungsi TypeScript)

Semua kode (controller, halaman, komponen) ada di dalam project kamu, bukan di vendor β€” bebas diubah. Starter kit tidak perlu β€œdi-update”; anggap sebagai titik awal.


2. Struktur Folder Penting

app/
β”œβ”€β”€ Http/Controllers/          # controller biasa, return Inertia::render(...)
β”œβ”€β”€ Http/Middleware/HandleInertiaRequests.php   # shared props (auth.user, dll.)
└── Actions/Fortify/           # logika register & reset password
routes/
β”œβ”€β”€ web.php                    # route halaman
└── settings.php               # halaman profil, password, appearance
resources/
β”œβ”€β”€ views/app.blade.php        # root template (satu-satunya Blade)
└── js/
    β”œβ”€β”€ app.tsx                # entry point Inertia
    β”œβ”€β”€ pages/                 # SATU file = SATU halaman Inertia
    β”‚   β”œβ”€β”€ welcome.tsx
    β”‚   β”œβ”€β”€ dashboard.tsx
    β”‚   β”œβ”€β”€ auth/login.tsx ...
    β”‚   └── settings/profile.tsx ...
    β”œβ”€β”€ layouts/               # app-layout, auth-layout (sidebar/header, simple/card/split)
    β”œβ”€β”€ components/            # komponen reusable + components/ui (shadcn)
    β”œβ”€β”€ hooks/
    β”œβ”€β”€ lib/                   # utilitas
    └── types/                 # definisi TypeScript (User, SharedData, dll.)

Nama di Inertia::render('settings/profile') = path file di resources/js/pages/settings/profile.tsx.


3. Menambah Halaman Baru (CRUD Sederhana)

Route & controller

php artisan make:model Product -mc
// routes/web.php
use App\Http\Controllers\ProductController;

Route::middleware(['auth', 'verified'])->group(function () {
    Route::get('products', [ProductController::class, 'index'])->name('products.index');
    Route::post('products', [ProductController::class, 'store'])->name('products.store');
});
// app/Http/Controllers/ProductController.php
use App\Models\Product;
use Illuminate\Http\Request;
use Inertia\Inertia;

class ProductController extends Controller
{
    public function index()
    {
        return Inertia::render('products/index', [
            'products' => Product::latest()->get(['id', 'name', 'price']),
        ]);
    }

    public function store(Request $request)
    {
        Product::create($request->validate([
            'name'  => ['required', 'string', 'max:255'],
            'price' => ['required', 'integer', 'min:0'],
        ]));

        return back();
    }
}

Tipe & halaman

// resources/js/types/product.ts
export interface Product {
  id: number
  name: string
  price: number
}
// resources/js/pages/products/index.tsx
import { Form, Head } from '@inertiajs/react'
import AppLayout from '@/layouts/app-layout'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import type { Product } from '@/types/product'

export default function ProductsIndex({ products }: { products: Product[] }) {
  return (
    <AppLayout breadcrumbs={[{ title: 'Produk', href: '/products' }]}>
      <Head title="Produk" />

      <div className="space-y-6 p-4">
        <Form action="/products" method="post" resetOnSuccess className="flex gap-2">
          {({ errors, processing }) => (
            <>
              <Input name="name" placeholder="Nama produk" />
              <Input name="price" type="number" placeholder="Harga" />
              <Button disabled={processing}>Tambah</Button>
              {errors.name && <p className="text-sm text-red-600">{errors.name}</p>}
            </>
          )}
        </Form>

        <ul className="divide-y">
          {products.map((p) => (
            <li key={p.id} className="flex justify-between py-2">
              <span>{p.name}</span>
              <span>Rp {p.price.toLocaleString('id-ID')}</span>
            </li>
          ))}
        </ul>
      </div>
    </AppLayout>
  )
}

Tambah komponen shadcn lain sesuai kebutuhan:

npx shadcn@latest add table dialog

Wayfinder (opsional, sudah terpasang di starter kit) membuat route Laravel bisa di-import sebagai fungsi TypeScript, sehingga typo URL ketahuan saat build, bukan saat runtime. Karena route di-generate saat build, route yang dihapus di Laravel akan membuat build frontend gagal jika masih direferensikan β€” itu fitur, bukan bug.


4. Autentikasi

Starter kit memakai Laravel Fortify sebagai backend autentikasi β€œheadless”: route & logika login, register, reset password, verifikasi email, konfirmasi password, dan two-factor authentication; halaman React-nya ada di resources/js/pages/auth/.

// config/fortify.php β€” nyalakan/matikan fitur
'features' => [
    Features::registration(),        // hapus baris ini untuk menutup registrasi publik
    Features::resetPasswords(),
    Features::emailVerification(),
    Features::twoFactorAuthentication(['confirm' => true, 'confirmPassword' => true]),
],

Logika register (misal menambah field phone) diubah di app/Actions/Fortify/CreateNewUser.php. Rate limit login diatur di FortifyServiceProvider.

Mengakses user di React lewat shared props:

import { usePage } from '@inertiajs/react'
import type { SharedData } from '@/types'

const { auth } = usePage<SharedData>().props

Alternatif: varian WorkOS AuthKit untuk social login, passkey, magic link, dan SSO tanpa menyimpan password sendiri.


5. Vite & TypeScript

  • vite.config.ts berisi laravel() plugin (input resources/js/app.tsx, refresh: true untuk reload saat file Blade/PHP berubah), plugin React, Tailwind, dan Wayfinder.
  • Alias @/ β†’ resources/js/ (diatur di tsconfig.json dan Vite).
  • Cek tipe: npm run types (atau npx tsc --noEmit). Lint & format: npm run lint, npm run format β€” cek package.json untuk nama script yang tersedia di versi kit kamu.
  • Environment variable untuk frontend harus berprefix VITE_ (VITE_APP_NAME) dan dibaca via import.meta.env.VITE_APP_NAME. Jangan taruh secret di sini β€” semuanya ikut ke bundle.

6. Build & Deployment

Di produksi tidak ada Vite dev server. Frontend di-build jadi file statis di public/build/ dengan manifest yang dibaca directive @vite.

# di server atau di pipeline CI
composer install --no-dev --optimize-autoloader
npm ci
npm run build                  # atau: npm run build:ssr jika memakai SSR
php artisan migrate --force
php artisan optimize           # cache config, route, view, event
php artisan queue:restart      # jika memakai queue worker

Checklist:

ItemKeterangan
APP_ENV=production, APP_DEBUG=falseWajib
Jangan commit public/build atau commit dengan sengajaPilih satu. Jika server tidak punya Node, build di CI lalu kirim artefaknya
Hapus file public/hotJika tertinggal, Laravel mengira dev server masih jalan β†’ asset 404
Web server document root β†’ public/Bukan root project
SSR (opsional)Jalankan php artisan inertia:start-ssr sebagai proses tetap (Supervisor)
Queue workerLihat Laravel Queue

Opsi hosting: VPS + Nginx/PHP-FPM (Forge/Ploi), Laravel Cloud, atau container (lihat Docker Compose untuk Dev Environment ).


7. Pitfalls

  • Halaman putih setelah deploy β†’ biasanya public/hot tertinggal atau npm run build lupa dijalankan.
  • Unable to locate file in Vite manifest β†’ entry di @vite(...) tidak cocok dengan input di vite.config.ts, atau build belum dijalankan.
  • Error TypeScript diabaikan β†’ Vite tidak melakukan type check saat build; jalankan tsc --noEmit di CI.
  • Nama komponen halaman salah kapital β†’ Inertia::render('Products/Index') vs file products/index.tsx bisa jalan di macOS (case-insensitive) tapi gagal di server Linux.
  • Menyimpan data server di state global (Redux/Zustand) β†’ biasanya tidak perlu; props Inertia sudah menjadi sumber data. Pakai state global hanya untuk state UI murni (lihat Redux jika memang dibutuhkan).
  • Menonaktifkan fitur Fortify tanpa menghapus referensinya di React β†’ build Wayfinder gagal.

Project nyata dengan kombinasi ini: Watch Store (Laravel + Inertia + React), Stackr (Laravel + React + TypeScript + Vite), Warung Online , dan Web Host Manager . Dasar React: Cara Menggunakan React .

Referensi: Starter Kits , Vite (Asset Bundling) , Fortify , Deployment .

Hey! I’m Fanny, the software engineer tending to this digital garden. You can read more about me, or subscribe by email.

Comments