Inertia.jsとは
フロントエンドをReactやVueで作りたい。でもAPIを別途設計・実装・管理するのは避けたい——そんなジレンマを解決するのが Inertia.js です。
Inertia はサーバーサイドのルーティングとコントローラーをそのままに、フロントエンドだけをReact・Vue・Svelteで書けるようにする「グルー(接着剤)」です。フレームワークではなく、既存のLaravelとJavaScriptフレームワークをつなぐアダプター層として機能します。
現在の最新バージョンはInertia v3(2026年3月26日リリース)です。Laravel 13のスターターキット(React・Vue・Svelte)はInertia対応済みです。
従来のSPA・MPAとの違い
| アーキテクチャ | 特徴 | 課題 |
|---|
| MPA(従来のBladeアプリ) | シンプル、Laravelとの統合が容易 | ページ遷移のたびにフルリロード |
| SPA(API + フロントエンド分離) | 高いインタラクション性 | API設計・認証・型定義の二重管理 |
| Inertia(モダンモノリス) | SPAのUX + サーバー側のシンプルさ | 独自の学習コストがある |
Inertia を使うと、Laravelのコントローラーから直接VueコンポーネントやReactコンポーネントにデータを渡せます。REST APIを定義する必要はなく、ページ遷移もブラウザのフルリロードではなくXHRで行われるため、SPA的なスムーズな操作感を実現できます。
Laravelスターターキットとの関係
laravel new でプロジェクトを作成する際にReact・Vue・Svelteを選ぶと、Inertiaが自動的にセットアップされます。
laravel new my-app
# 対話式プロンプトでReact / Vue / Svelteを選ぶとInertia構成になる
スターターキットでは以下の構成が自動で用意されます。
inertiajs/inertia-laravel(サーバーサイドアダプター)
@inertiajs/react / @inertiajs/vue3 / @inertiajs/svelte(クライアントアダプター)
HandleInertiaRequests ミドルウェア
- ログイン・登録・パスワードリセットなどの認証画面(すでにInertiaコンポーネントで実装済み)
既存プロジェクトに手動でInertiaを導入する場合はサーバーサイドとクライアントサイドを別々にインストールします。詳細は公式ドキュメントのインストール手順を参照してください。
Inertia::render() の基本
Laravelのコントローラーから Inertia レスポンスを返すには Inertia::render() を使います。第一引数にJavaScriptコンポーネント名、第二引数にプロップとして渡すデータを指定します。
use Inertia\Inertia;
class PostController extends Controller
{
public function index()
{
return Inertia::render('Posts/Index', [
'posts' => Post::latest()->paginate(10),
]);
}
public function show(Post $post)
{
return Inertia::render('Posts/Show', [
'post' => $post->only('id', 'title', 'content', 'created_at'),
'author' => $post->user->only('id', 'name'),
]);
}
}
Inertia::render() の代わりに inertia() ヘルパー関数も使えます。どちらを使うかはチームのスタイルで統一してください。
コンポーネント名 'Posts/Index' はファイルパスに対応します。Reactなら resources/js/Pages/Posts/Index.jsx、Vueなら resources/js/Pages/Posts/Index.vue が対応するコンポーネントです。
ページコンポーネントの構造
Inertiaのページコンポーネントは通常のVue/Reactコンポーネントです。コントローラーから渡したデータがプロップとして受け取れます。
// resources/js/Pages/Posts/Index.jsx
import { Link } from '@inertiajs/react'
export default function PostsIndex({ posts }) {
return (
<div>
<h1>投稿一覧</h1>
{posts.data.map(post => (
<article key={post.id}>
<h2>
<Link href={`/posts/${post.id}`}>{post.title}</Link>
</h2>
</article>
))}
</div>
)
}
<!-- resources/js/Pages/Posts/Index.vue -->
<script setup>
import { Link } from '@inertiajs/vue3'
defineProps({
posts: Object,
})
</script>
<template>
<div>
<h1>投稿一覧</h1>
<article v-for="post in posts.data" :key="post.id">
<h2>
<Link :href="`/posts/${post.id}`">{{ post.title }}</Link>
</h2>
</article>
</div>
</template>
<Link> コンポーネントを使うと、ページ遷移がXHRで行われ、フルページリロードを回避できます。通常の <a> タグとまったく同じように書けますが、裏側でInertiaがページコンポーネントだけを差し替えます。
共有データ — HandleInertiaRequests ミドルウェア
すべてのページで共通して必要なデータ(ログイン中のユーザー情報・フラッシュメッセージなど)は、HandleInertiaRequests ミドルウェアの share() メソッドで定義します。
// app/Http/Middleware/HandleInertiaRequests.php
use Illuminate\Http\Request;
use Inertia\Middleware;
class HandleInertiaRequests extends Middleware
{
public function share(Request $request): array
{
return array_merge(parent::share($request), [
'appName' => config('app.name'),
'auth' => [
'user' => $request->user()
? $request->user()->only('id', 'name', 'email')
: null,
],
'flash' => [
'success' => $request->session()->get('success'),
'error' => $request->session()->get('error'),
],
]);
}
}
共有データは自動的にすべてのページのプロップにマージされます。
// レイアウトコンポーネントからアクセスする例
import { usePage } from '@inertiajs/react'
export default function Layout({ children }) {
const { auth, flash } = usePage().props
return (
<main>
<header>
{auth.user ? `ログイン中: ${auth.user.name}` : 'ゲスト'}
</header>
{flash.success && <div className="alert-success">{flash.success}</div>}
<article>{children}</article>
</main>
)
}
共有データはすべてのリクエストに含まれるため、必要最低限のデータに絞ることを推奨します。fn() を使ったレイジー評価にすると、実際に必要なリクエストでのみ評価されます。
フォーム送信とバリデーションエラーの処理
Inertiaでのフォーム処理はLaravelのバリデーションと自然に統合されます。useForm() ヘルパーを使うと、フォームの状態管理・送信・エラー表示がシンプルに実装できます。
コントローラー側
class PostController extends Controller
{
public function store(Request $request)
{
$validated = $request->validate([
'title' => ['required', 'string', 'max:255'],
'content' => ['required', 'string'],
]);
Post::create($validated + ['user_id' => auth()->id()]);
return redirect()->route('posts.index')
->with('success', '投稿を作成しました。');
}
}
バリデーションエラーが発生した場合、Laravelは自動的にフォームページにリダイレクトし、エラー情報をセッションに保存します。Inertiaはこれを自動的に検知して errors プロップとしてページに渡します。
フロントエンド側
// resources/js/Pages/Posts/Create.jsx
import { useForm } from '@inertiajs/react'
export default function PostsCreate() {
const { data, setData, post, processing, errors } = useForm({
title: '',
content: '',
})
function handleSubmit(e) {
e.preventDefault()
post('/posts')
}
return (
<form onSubmit={handleSubmit}>
<div>
<label>タイトル</label>
<input
value={data.title}
onChange={e => setData('title', e.target.value)}
/>
{errors.title && <p className="error">{errors.title}</p>}
</div>
<div>
<label>本文</label>
<textarea
value={data.content}
onChange={e => setData('content', e.target.value)}
/>
{errors.content && <p className="error">{errors.content}</p>}
</div>
<button type="submit" disabled={processing}>
{processing ? '送信中...' : '投稿する'}
</button>
</form>
)
}
<!-- resources/js/Pages/Posts/Create.vue -->
<script setup>
import { useForm } from '@inertiajs/vue3'
const form = useForm({
title: '',
content: '',
})
function submit() {
form.post('/posts')
}
</script>
<template>
<form @submit.prevent="submit">
<div>
<label>タイトル</label>
<input v-model="form.title" />
<p v-if="form.errors.title" class="error">{{ form.errors.title }}</p>
</div>
<div>
<label>本文</label>
<textarea v-model="form.content"></textarea>
<p v-if="form.errors.content" class="error">{{ form.errors.content }}</p>
</div>
<button type="submit" :disabled="form.processing">
{{ form.processing ? '送信中...' : '投稿する' }}
</button>
</form>
</template>
バリデーションエラーが返ったとき、useForm() は入力内容を保持したままエラーを表示します。フォームを再入力させる手間がなく、ユーザー体験が向上します。
向いているユースケース・向いていないケース
Inertiaが向いているケース
- Laravelをよく知っているチームがSPA的なUIを作りたいとき
- 認証・認可・バリデーションをLaravel側で一元管理したいとき
- 管理画面・社内ツールなど、SEOの優先度が低いアプリ
- APIを別途設計・管理するコストを避けたいプロジェクト
向いていないケース
- 外部の複数クライアント(モバイルアプリなど)が同じAPIを使う場合
- SEOが非常に重要なコンテンツサイト(SSRで対応可能だが複雑になる)
- マイクロフロントエンド構成など、フロントエンドが完全に独立したチームで開発される場合
Inertia はサーバーサイドレンダリング(SSR)もサポートしています。SEOが必要なページがある場合は SSR オプションを検討してください。Laravel のスターターキットでも SSR 設定が組み込まれています。
Inertia v3 の主な変更点
Inertia v3 は2026年3月26日にリリースされました。v2からの主な変更点を紹介します。
Axios を廃止 — 軽量な組み込みXHRクライアントへ
v3 では Axios が廃止され、より軽量な組み込みXHRクライアントに置き換えられました。ほとんどのアプリケーションでコード変更は不要です。Axios のインターセプターは組み込みインターセプターに直接移行できます。引き続き Axios を使いたい場合は Axios アダプター経由で利用できます。
@inertiajs/vite プラグインによるSSRのシンプル化
新しい Vite プラグインを導入すると、ページコンポーネントの自動解決とSSR設定が大幅に簡略化されます。開発中のSSRは npm run dev を実行するだけで動作するようになり、別途Nodeサーバーを起動する必要がなくなりました。
npm install @inertiajs/vite@^3.0
useHttp フック — ページ遷移を伴わないHTTPリクエスト
useHttp フックを使うと、ページ遷移(Inertia visit)を発生させずにサーバーへHTTPリクエストを送れます。モーダルの保存やバックグラウンド処理など、現在のページを維持したまま通信したい場面で役立ちます。
楽観的UI更新(Optimistic Updates)
useForm およびルーターレベルで楽観的な更新がサポートされました。サーバーの応答を待たずにUIを即座に更新し、失敗した場合は自動的にロールバックします。
レイアウトプロップ(Layout Props)
useLayoutProps フックを使うと、ページコンポーネントから永続レイアウトへデータを渡せるようになりました。従来は共有データかページプロップに頼るしかなかった「特定ページのレイアウト状態制御」がシンプルに書けます。
要件の変更
v3 では以下の最低バージョンが引き上げられました。
| 項目 | v2 | v3 |
|---|
| PHP | 8.1+ | 8.2+ |
| Laravel | 10+ | 11+ |
| React | 18+ | 19+ |
| Svelte | 4+ | 5+ |
Inertia::lazy() の廃止
v2 で非推奨になっていた Inertia::lazy() が v3 で完全に削除されました。代わりに Inertia::optional() を使います。
// v2(非推奨)
'users' => Inertia::lazy(fn () => User::all()),
// v3
'users' => Inertia::optional(fn () => User::all()),
イベント名の変更
| v2 | v3 |
|---|
invalid | httpException |
exception | networkError |
future オプションの廃止
v2 で実験的オプションとして提供されていた future 設定ブロックが廃止されました。これらのオプションはすべてデフォルトで有効になっています。createInertiaApp の設定から future ブロックを削除してください。
v2 からのアップグレード手順の詳細は 公式アップグレードガイド を参照してください。v2 は2026年9月26日までバグ修正が、2027年3月26日までセキュリティ修正が提供されます。
まとめ
Inertia.js は「LaravelはそのままでフロントエンドをReact/Vueで書きたい」という現実的なニーズに応えるツールです。APIを設計せずに、コントローラーから直接コンポーネントにデータを渡せるシンプルさが最大の強みです。
Laravel 13 のスターターキットが Inertia に対応していることからも、Laravel エコシステムにおける Inertia の位置づけは確立されています。Livewire がPHPだけで動的UIを作るアプローチなら、Inertia はJavaScriptフレームワークのパワーをサーバー側のシンプルさを損なわずに使えるアプローチです。
どちらを選ぶかはチームのスキルセットとプロジェクトの要件次第ですが、「LaravelのコントローラーをそのままにReactで画面を作りたい」という場面では Inertia が最も自然な選択です。
Inertia.js公式ドキュメント
Inertia v3の全機能については公式ドキュメントを参照してください。