1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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 は独立したフレームワークではなく、サーバーとクライアントを繋ぐ接着剤です。

  1. 初回アクセス: 通常通りサーバーが HTML を返す(Blade のレイアウトの中に @inertia があるだけ)
  2. 以降の遷移: <Link> のクリックやフォーム送信は XHR になり、サーバーは HTML ではなく「コンポーネント名 + props」の JSON(page object)を返す
  3. 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 で返して、フロントで整形して、フィールドごとにマッピングして……という定型作業が丸ごと消えます。

useFormprocessing(送信中)、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 --ssrphp artisan inertia:start-ssr)は変更なし。
  • useHttp フック — ページ遷移を伴わない単発の HTTP リクエスト用。useForm と同じ書き味で、リアクティブな状態・エラーハンドリング・アップロード進捗・キャンセルが使えます。検索サジェストや外部 API 呼び出しに。
  • Optimistic Updates が標準機能に — router visit や useFormoptimistic() を挟むと、サーバー応答を待たずに props を先行更新し、失敗時は自動でロールバックしてくれます。いいねボタンやトグルの類が素直に書ける。
  • Instant Visits — リクエストをバックグラウンドで飛ばしつつ、遷移先コンポーネントを即座に描画。共有 props だけ先に見せて、ページ固有の props が後から合流します。
  • Layout PropsuseLayoutProps でレイアウト側がデフォルト値を宣言し、ページ側が 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 分ほど触ってみるのをおすすめします。

参考リンク:

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?