September 27, 2026
Inertia.js.
Cara kerja Inertia.js: routing & controller tetap di Laravel, halaman dirender oleh React/Vue/Svelte. Membahas props, shared data, form, partial reload, deferred props, dan perubahan di Inertia 3.
Inertia.js adalah βlemβ antara backend klasik (Laravel) dan frontend modern (React , Vue, Svelte). Kamu tetap menulis route dan controller di Laravel seperti biasa, tapi alih-alih mengembalikan Blade view, controller mengembalikan komponen halaman JavaScript beserta props-nya. Hasilnya terasa seperti SPA tanpa perlu membangun REST API, state management untuk data server, atau router di sisi client.
Versi: Inertia 3 rilis stabil Maret 2026 (Vite plugin, tanpa Axios, optimistic update). Starter kit Laravel dan banyak project masih memakai Inertia 2 β konsep di catatan ini sama, perbedaan utama ditandai.
1. Cara Kerja
Kunjungan pertama:
Browser ββGET /postsβββΆ Laravel βββΆ HTML lengkap + <div data-page='{"component":"posts/index","props":{...}}'>
ββ JS mem-boot React dan merender posts/index
Navigasi berikutnya (klik <Link>):
Browser ββXHR GET /posts/1 (header X-Inertia: true)βββΆ Laravel
βββ JSON { component: "posts/show", props: {...}, url, version }
ββ Inertia menukar komponen halaman tanpa full reload
| Tanggung jawab | Dikerjakan oleh |
|---|---|
| Routing, middleware, auth, validasi, query DB | Laravel |
| Rendering UI, interaktivitas, transisi halaman | React/Vue/Svelte |
| Menyambungkan keduanya (XHR, history, error validasi) | Inertia |
Inertia bukan framework dan bukan pengganti API. Kalau kamu butuh API publik untuk mobile app, tetap buat API terpisah.
2. Instalasi (Laravel + React)
Cara termudah adalah memakai starter kit (laravel new β pilih React), lihat Laravel + React
. Instalasi manual:
Server side
composer require inertiajs/inertia-laravel
php artisan inertia:middleware
// bootstrap/app.php
use App\Http\Middleware\HandleInertiaRequests;
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [
HandleInertiaRequests::class,
]);
})
Root template resources/views/app.blade.php:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
@viteReactRefresh
@vite('resources/js/app.tsx')
<x-inertia::head />
</head>
<body>
<x-inertia::app />
</body>
</html>
(Di Inertia 2 dan sebelumnya memakai directive @inertiaHead dan @inertia β masih didukung.)
Client side (Inertia 3)
npm install react react-dom @vitejs/plugin-react @inertiajs/react @inertiajs/vite
// vite.config.js
import { defineConfig } from 'vite'
import laravel from 'laravel-vite-plugin'
import react from '@vitejs/plugin-react'
import inertia from '@inertiajs/vite'
export default defineConfig({
plugins: [
laravel({ input: ['resources/js/app.tsx'], refresh: true }),
inertia(),
react(),
],
})
// resources/js/app.tsx
import { createInertiaApp } from '@inertiajs/react'
createInertiaApp() // plugin otomatis mencari halaman di ./pages atau ./Pages
Di Inertia 2, kamu menulis sendiri callback resolve (biasanya dengan import.meta.glob('./pages/**/*.tsx')) dan setup. Cara ini masih didukung di v3 bila tidak memakai plugin Vite.
3. Render Halaman & Props
// routes/web.php
use App\Models\Post;
use Inertia\Inertia;
Route::get('/posts', function () {
return Inertia::render('posts/index', [
'posts' => Post::with('user:id,name')
->latest()
->paginate(10)
->through(fn ($p) => [
'id' => $p->id,
'title' => $p->title,
'author' => $p->user->name,
]),
]);
})->name('posts.index');
// shortcut untuk halaman tanpa data
Route::inertia('/about', 'about');
// resources/js/pages/posts/index.tsx
import { Head, Link } from '@inertiajs/react'
type Post = { id: number; title: string; author: string }
export default function Index({ posts }: { posts: { data: Post[] } }) {
return (
<>
<Head title="Posts" />
{posts.data.map((post) => (
<Link key={post.id} href={`/posts/${post.id}`} prefetch>
{post.title} β {post.author}
</Link>
))}
</>
)
}
Penting: semua props dikirim ke browser sebagai JSON dan bisa dilihat di page source. Jangan kirim model utuh yang berisi kolom sensitif; pilih field yang dibutuhkan saja (only(), through(), atau API Resource).
4. Shared Data
Data yang dibutuhkan di semua halaman (user login, nama aplikasi, notifikasi) didefinisikan di middleware HandleInertiaRequests:
public function share(Request $request): array
{
return [
...parent::share($request),
'appName' => config('app.name'),
'auth' => [
'user' => fn () => $request->user()?->only('id', 'name', 'email'),
],
];
}
import { usePage } from '@inertiajs/react'
export default function Navbar() {
const { auth } = usePage<{ auth: { user: { name: string } | null } }>().props
return <span>{auth.user ? `Halo, ${auth.user.name}` : 'Tamu'}</span>
}
Bungkus nilai dengan closure (fn () =>) agar hanya dievaluasi saat dibutuhkan. Untuk pesan sekali tampil (toast βBerhasil disimpanβ), gunakan flash data, bukan shared data permanen.
5. Form
Inertia punya dua cara: komponen <Form> dan hook useForm. Keduanya menangani error validasi Laravel secara otomatis β ketika validasi gagal, Laravel redirect back dan error muncul di errors.
Komponen <Form>
import { Form } from '@inertiajs/react'
export default function Create() {
return (
<Form action="/posts" method="post" resetOnSuccess>
{({ errors, processing }) => (
<>
<input name="title" />
{errors.title && <p className="text-red-600">{errors.title}</p>}
<textarea name="body" />
<button disabled={processing}>
{processing ? 'Menyimpan...' : 'Simpan'}
</button>
</>
)}
</Form>
)
}
Hook useForm
import { useForm } from '@inertiajs/react'
export default function Create() {
const { data, setData, post, processing, errors, reset } = useForm({
title: '',
body: '',
})
function submit(e: React.FormEvent) {
e.preventDefault()
post('/posts', { onSuccess: () => reset() })
}
return (
<form onSubmit={submit}>
<input value={data.title} onChange={(e) => setData('title', e.target.value)} />
{errors.title && <p>{errors.title}</p>}
<button disabled={processing}>Simpan</button>
</form>
)
}
Di controller, cukup validasi dan redirect seperti aplikasi Blade biasa:
public function store(Request $request)
{
$validated = $request->validate([
'title' => ['required', 'max:255'],
'body' => ['required'],
]);
$request->user()->posts()->create($validated);
return to_route('posts.index');
}
6. Partial Reload & Deferred Props
Partial reload meminta ulang sebagian props saja β berguna untuk filter, polling, atau tombol refresh.
import { router, Link } from '@inertiajs/react'
router.reload({ only: ['posts'] })
<Link href="/posts?status=draft" only={['posts']}>Draft</Link>
Di server, props yang mahal sebaiknya dibungkus supaya tidak dihitung kalau tidak diminta:
return Inertia::render('dashboard', [
'stats' => fn () => $this->stats(), // dievaluasi hanya jika diminta
'users' => Inertia::optional(fn () => User::all()), // HANYA saat diminta lewat `only`
'notice' => Inertia::always(fn () => $this->notice()), // selalu ikut, termasuk partial reload
'reports' => Inertia::defer(fn () => Report::heavy()), // dimuat setelah halaman tampil
]);
import { Deferred } from '@inertiajs/react'
<Deferred data="reports" fallback={<div>Memuat laporan...</div>}>
<ReportTable />
</Deferred>
(Inertia::lazy() dari versi lama sudah digantikan Inertia::optional().)
7. Hal Baru di Inertia 3 (Ringkas)
| Fitur | Keterangan |
|---|---|
Vite plugin @inertiajs/vite | Resolusi halaman & SSR otomatis, createInertiaApp() jadi sangat ringkas |
| Tanpa Axios | Memakai HTTP client bawaan; Axios tetap bisa lewat adapter |
useHttp | Request HTTP biasa (bukan navigasi halaman) dengan state seperti useForm |
| Optimistic updates | UI diperbarui dulu sebelum respons server, otomatis di-rollback jika gagal |
| Layout props | Mengirim data ke persistent layout |
Syarat adapter Laravel v3: minimal Laravel 11. Saat upgrade dari v2, baca upgrade guide resmi karena ada perubahan konfigurasi.
8. Pitfalls
- Membocorkan data lewat props (password hash, token, kolom internal). Selalu pilih field.
- N+1 query di props β Inertia tidak menyembunyikan masalah Eloquent
; pakai
with(). - Pakai
<a href>biasa β full page reload. Gunakan<Link>. - Redirect ke domain luar / halaman non-Inertia dari request Inertia β gunakan
Inertia::location($url). - Asset berubah setelah deploy β Inertia mendeteksi perbedaan
version()dan melakukan full reload; pastikan build Vite dijalankan saat deploy. - Butuh SEO untuk halaman publik β aktifkan SSR (
npm run build:ssr) atau render halaman tersebut dengan Blade. - Butuh klien selain web (mobile app, integrasi pihak ketiga) β Inertia saja tidak cukup, buat API.
Pengalaman nyata: Watch Store dibangun dengan Laravel + Inertia + React, sementara Kos Management App justru dimigrasi dari monolith Inertia ke REST API + SPA terpisah karena kebutuhan multi-tenant dan frontend yang lebih independen.
Referensi: Inertia.js docs v3 , Forms , Partial reloads , Shared data .

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