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:
| Pendekatan | Cara kerja | Cocok untuk |
|---|---|---|
| Monolith Inertia | Laravel mengurus routing & data, React merender halaman (lihat Inertia.js ) | Aplikasi web tim kecil, dashboard, e-commerce |
| API + SPA terpisah | Laravel 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:
| Lapisan | Teknologi |
|---|---|
| Backend | Laravel 13, Fortify (autentikasi) |
| Jembatan | Inertia |
| Frontend | React 19 + TypeScript |
| Styling & UI | Tailwind CSS 4 + shadcn/ui |
| Bundler | Vite + laravel-vite-plugin |
| Routing type-safe | Wayfinder (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.tsberisilaravel()plugin (inputresources/js/app.tsx,refresh: trueuntuk reload saat file Blade/PHP berubah), plugin React, Tailwind, dan Wayfinder.- Alias
@/βresources/js/(diatur ditsconfig.jsondan Vite). - Cek tipe:
npm run types(ataunpx tsc --noEmit). Lint & format:npm run lint,npm run formatβ cekpackage.jsonuntuk nama script yang tersedia di versi kit kamu. - Environment variable untuk frontend harus berprefix
VITE_(VITE_APP_NAME) dan dibaca viaimport.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:
| Item | Keterangan |
|---|---|
APP_ENV=production, APP_DEBUG=false | Wajib |
Jangan commit public/build atau commit dengan sengaja | Pilih satu. Jika server tidak punya Node, build di CI lalu kirim artefaknya |
Hapus file public/hot | Jika 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 worker | Lihat 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/hottertinggal ataunpm run buildlupa dijalankan. Unable to locate file in Vite manifestβ entry di@vite(...)tidak cocok denganinputdivite.config.ts, atau build belum dijalankan.- Error TypeScript diabaikan β Vite tidak melakukan type check saat build; jalankan
tsc --noEmitdi CI. - Nama komponen halaman salah kapital β
Inertia::render('Products/Index')vs fileproducts/index.tsxbisa 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.