Laravel の Inertia.js とは何か — APIを書かずに React で SPA を作る
結論
Inertia.js は「Laravel のコントローラをそのまま使いながら、View だけ React / Vue / Svelte に差し替える」ためのライブラリです。REST API も React Router も書かずに SPA が作れます。公式は自らを モダンモノリス(modern monolith) と呼んでいます。
「SPA にしたいけど、API を設計して認証を JWT にして状態管理を入れて……という一式が正直しんどい」——そう思ったことがあるなら、Inertia は検討する価値があります。
何が問題だったのか
Laravel + React で SPA を作ろうとすると、普通はこうなります。
- Laravel 側:
routes/api.phpに REST API を生やす、Sanctum で認証、API Resource で整形 - React 側: React Router でルーティング、axios でフェッチ、TanStack Query や Redux で状態管理、バリデーションエラーの受け渡しを自前で実装
ユーザー一覧を出すだけの CRUD でも、同じことを2箇所に書く羽目になります。ルーティング定義がサーバーとクライアントの両方にあり、片方を直すともう片方も直す。認可も二重管理。API のバージョニングも考える。
このオーバーヘッドは、外部クライアント(モバイルアプリなど)がある場合には正当な投資です。でも「社内の管理画面」「Web で完結する業務アプリ」だと、単に二度手間なだけです。
Inertia はここを潰します。
仕組み
Inertia は独立したフレームワークではなく、サーバーとクライアントを繋ぐ接着剤です。
-
初回アクセス: 通常通りサーバーが HTML を返す(Blade のレイアウトの中に
@inertiaがあるだけ) -
以降の遷移:
<Link>のクリックやフォーム送信は XHR になり、サーバーは HTML ではなく「コンポーネント名 + props」の JSON(page object)を返す - Inertia がクライアント側で該当コンポーネントを差し替える
つまり ルーティングはサーバー側のまま、画面遷移だけクライアントサイド という構成です。ここが Next.js や Nuxt と決定的に違うところで、routes/web.php が唯一の真実であり続けます。
実際のコード
サーバー側
// routes/web.php
Route::get('/users', [UserController::class, 'index']);
Route::post('/users', [UserController::class, 'store']);
// app/Http/Controllers/UserController.php
use Inertia\Inertia;
public function index(Request $request)
{
return Inertia::render('Users/Index', [
'users' => User::query()
->when($request->search, fn ($q, $s) => $q->where('name', 'like', "%{$s}%"))
->paginate(20),
'filters' => $request->only('search'),
]);
}
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'max:50'],
'email' => ['required', 'email', 'unique:users'],
]);
User::create($request->only('name', 'email'));
return to_route('users.index')->with('success', '登録しました');
}
見ての通り、普通の Laravel コントローラです。view() が Inertia::render() に変わっただけ。バリデーションもリダイレクトもフラッシュメッセージも、いつも通りの書き方でいい。
クライアント側
// resources/js/Pages/Users/Index.jsx
import { Link, useForm, router } from '@inertiajs/react'
export default function Index({ users, filters }) {
// ← コントローラで渡した配列がそのまま props で降ってくる
const { data, setData, post, processing, errors, reset } = useForm({
name: '',
email: '',
})
const submit = () => {
post('/users', { onSuccess: () => reset() })
}
return (
<div>
<input
defaultValue={filters.search ?? ''}
onChange={(e) =>
router.get('/users', { search: e.target.value }, {
preserveState: true, // 入力中のstateを保持
replace: true, // 履歴を汚さない
})
}
/>
{users.data.map((u) => (
<Link key={u.id} href={`/users/${u.id}`}>{u.name}</Link>
))}
<input value={data.name} onChange={(e) => setData('name', e.target.value)} />
{errors.name && <span className="text-red-600">{errors.name}</span>}
<input value={data.email} onChange={(e) => setData('email', e.target.value)} />
{errors.email && <span className="text-red-600">{errors.email}</span>}
<button onClick={submit} disabled={processing}>保存</button>
</div>
)
}
注目すべきは errors です。Laravel の $request->validate() が投げたエラーが、そのまま React の props に流れてきます。バリデーションエラーを JSON で返して、フロントで整形して、フィールドごとにマッピングして……という定型作業が丸ごと消えます。
useForm は processing(送信中)、progress(アップロード進捗)、recentlySuccessful(送信直後フラグ)なども面倒を見てくれます。この「フォーム周りの体験の良さ」が、実は Inertia を選ぶ最大の理由かもしれません。
セットアップ
composer require inertiajs/inertia-laravel
php artisan inertia:middleware
npm install @inertiajs/react @inertiajs/vite
vite.config.js にプラグインを追加して、
import inertia from '@inertiajs/vite'
import laravel from 'laravel-vite-plugin'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
laravel({ input: ['resources/js/app.jsx'], refresh: true }),
react(),
inertia(),
],
})
エントリポイントはこれだけです。
// resources/js/app.jsx
import { createInertiaApp } from '@inertiajs/react'
createInertiaApp()
Vite プラグインがページ解決・マウント・SSR を自動で組み立ててくれるため、v2 まで必要だった resolve / setup のボイラープレートが不要になりました。手動で書きたい場合は従来通りコールバックを渡せます。
ルートテンプレートは Blade を1枚だけ用意します。
{{-- resources/views/app.blade.php --}}
<!DOCTYPE html>
<html>
<head>
@vite(['resources/css/app.css', 'resources/js/app.jsx'])
@inertiaHead
</head>
<body>
@inertia
</body>
</html>
なお Laravel のスターターキットで React / Vue を選ぶと、この構成が最初から組まれた状態で生成されます。素振りするなら laravel new が一番早いです。
v3 で何が変わったか(2026年3月リリース)
Inertia.js v3 は2026年3月26日に安定版がリリースされました。単なるマイナーアップではなく、依存関係とセットアップの作り直しを含む大きな更新です。
主な変更点:
-
Axios が依存から外れた — 独自の XHR クライアントを内蔵。約15KB(gzip)分バンドルが軽くなります。インターセプタは組み込みクライアント側(
http.onRequest()など)に移行するか、axiosAdapterで従来通り Axios を使うこともできます。qsパッケージも同様に外れました。 -
Vite プラグインの導入 — 前述の通り、エントリポイントが
createInertiaApp()の1行で済むように。 -
開発時の SSR が別プロセス不要に —
npm run devでそのまま SSR が効きます。本番のワークフロー(vite build --ssr→php artisan inertia:start-ssr)は変更なし。 -
useHttpフック — ページ遷移を伴わない単発の HTTP リクエスト用。useFormと同じ書き味で、リアクティブな状態・エラーハンドリング・アップロード進捗・キャンセルが使えます。検索サジェストや外部 API 呼び出しに。 -
Optimistic Updates が標準機能に — router visit や
useFormにoptimistic()を挟むと、サーバー応答を待たずに props を先行更新し、失敗時は自動でロールバックしてくれます。いいねボタンやトグルの類が素直に書ける。 - Instant Visits — リクエストをバックグラウンドで飛ばしつつ、遷移先コンポーネントを即座に描画。共有 props だけ先に見せて、ページ固有の props が後から合流します。
-
Layout Props —
useLayoutPropsでレイアウト側がデフォルト値を宣言し、ページ側がsetLayoutProps()で上書きできます。イベントバスや provide/inject の回避策が不要に。
破壊的変更として、v3 は PHP 8.2+ / Laravel 11+ / React 19+(React アダプタの場合)を要求します。 全パッケージが ESM only になり、CommonJS の require() は使えません。Inertia::lazy()(v2 で非推奨)は削除され Inertia::optional() に、router.cancel() は router.cancelAll() に変わっています。既存プロジェクトを上げる場合は公式のアップグレードガイドを一読してからにしましょう。
なお v2 系も、前メジャーとしてリリースから6ヶ月のバグ修正・12ヶ月のセキュリティ修正が受けられます。急いで上げる必要はありませんが、Laravel 10 以下に留まっているアプリは「まず Laravel を上げる」が先の作業になります。
使うべきか、使わないべきか
| 選択肢 | 向いているケース | 向いていないケース |
|---|---|---|
| Inertia | Web で完結する業務アプリ・管理画面、少人数開発、Laravel の資産を活かしたい | モバイルアプリや外部サービスから叩く API が必要 |
| API 分離(Laravel + React SPA) | フロント/バックが別チーム・別デプロイ、複数クライアントを抱える | 単純な CRUD 中心だと二度手間 |
| Livewire | React/Vue を書きたくない、PHP で完結させたい | リッチな UI、複雑なクライアント状態、npm エコシステムの部品を使いたい |
判断軸はシンプルで、「このアプリのクライアントは Web ブラウザだけか?」 です。
Yes なら Inertia でいい。将来モバイルアプリを出すかもしれない、という程度の不確実性のために最初から API を分離するのは、大抵オーバーエンジニアリングになります。必要になった時点で routes/api.php を追加すればいいだけで、Inertia は API の併設を妨げません。
No なら素直に API を分離しましょう。Inertia のレスポンスは Inertia 専用のフォーマットなので、外部クライアントからは使えません。
Livewire との比較は好みの領域に入ってきますが、「チャート、リッチテキストエディタ、複雑なドラッグ&ドロップ」といった npm エコシステムの部品を多用するなら Inertia、フォームとテーブルが中心なら Livewire、くらいのざっくり感で問題ないと思います。
まとめ
Inertia.js は「SPA の UX が欲しいだけなのに、なぜ2つのアプリを作らないといけないのか」という素朴な不満に対する答えです。
- ルーティング・認証・認可・バリデーションは Laravel のまま
- View だけ React / Vue / Svelte
- API 設計もクライアントルーティングも 不要
v3 で Axios 依存が外れ、セットアップも数行まで削られました。Laravel のスターターキットに乗るのが一番手軽なので、まずは laravel new して 30 分ほど触ってみるのをおすすめします。
参考リンク: