Laravel

LaravelとReactの連携方法|3つの構成の選び方とInertia・Sanctumの設定【Laravel 13】

LaravelとReactの連携方法|3つの構成の選び方とInertia・Sanctumの設定【Laravel 13】

LaravelとReactは競合する技術ではありません。LaravelはPHPで書くサーバー側のフレームワーク、ReactはブラウザのUIを組むJavaScriptライブラリで、実務で決めるのは「どちらを使うか」ではなく「どうつなぐか」です。この記事では、つなぎ方を3つの構成に分け、2026年9月28日時点の公式Reactスターターキット(Laravel 13.33.0・Inertia 3・React 19.3.0)で手順とサーバーの応答を確かめた結果をもとに、構成の選び方から認証・SSR・デプロイまでを整理します。

まとめ:LaravelとReactの連携構成の選び方

  • 新規にWebアプリを作るなら、公式Reactスターターキット(Inertia)が第一候補です。APIを作らずにコントローラーからReactのページへデータを渡せます。
  • Next.jsなど別アプリのReactや、モバイルアプリも同じバックエンドを使うなら、LaravelをAPIにしてSanctumで認証します。Cookie認証は statefulApi() を足さないと401になります。
  • 既存のBladeアプリに一部だけReactを入れるなら、@vitejs/plugin-react と @viteReactRefresh で部分導入します。
  • スターターキットを composer create-project で直接作るときは --stability=dev が必要です。付けないと2025年2月の古い版が入ります。
  • 検索に表示されるページでInertiaを使うなら、SSR(npm run build:ssr と php artisan inertia:start-ssr)を有効にします。

以下、役割分担の整理から順に、各構成の手順と落とし穴を説明します。

LaravelとReactの役割分担と「laravel vs react」の比較軸

LaravelとReactを比較するときは、サーバー側とUI側という役割の違いを押さえます。LaravelはルーティングやEloquent(データベース操作)、認証、キュー、メール送信を担うサーバー側の土台です。本記事のInertia・Blade部分導入では、Reactが画面の描画と状態管理を担当し、データベース操作やセッション管理はLaravel側に置きます。

比べるべきなのは「Laravelの画面を何で書くか」です。Laravel 13の公式ドキュメント(Frontend)は、PHPで書く道としてBladeとLivewire、JavaScriptで書く道としてInertia経由のReact・Vue・Svelteを挙げています。

画面の書き方 主に書く言語 公式スターターキット 向くチーム
Blade PHP(テンプレート) なし(素のLaravel) 画面の動きが少ない業務画面
Livewire PHP あり PHP中心でJSを書きたくない
React(Inertia) TypeScript あり React経験者がいる・部品が多い
Vue(Inertia) TypeScript あり Vue経験者がいる
Svelte(Inertia) TypeScript あり Svelte 5を使いたい

React・Vue・Svelteの3つはどれもInertia 3の上に載るので、Laravel側のコントローラーやルートの書き方は同じです。差が出るのはフロントのエコシステムで、UI部品のshadcn/uiやReact向けライブラリを使いたいならReact、チームにVueの経験者が多いならVueを選びます。JavaScriptを書く人がいないなら、Reactを無理に入れずにLivewire(v4)を選ぶ方が保守しやすくなります。

3つの連携構成の比較(Inertia・API分離・Blade部分導入)

LaravelとReactのつなぎ方は、ルーティングをどちらが持つかで3つに分かれます。

比較軸 Inertia(スターターキット) API分離+Sanctum Blade部分導入
ルーティング Laravel React側(Next.js等) Laravel
データの渡し方 props(JSON) REST API data属性など
認証 セッション(Fortify) Sanctum セッション
リポジトリ 通常1つ 同一でも分割でも可 通常1つ
APIの設計 不要 必要 不要(必要なら追加)
モバイルアプリとの共用 不向き 向く 不向き

判断の基準は2つだけです。1つ目は、Laravel以外のクライアント(スマホアプリ、別チームのNext.js、外部連携)が同じデータを読むかどうかです。読むなら外部クライアント向けのAPIが必要ですが、Web画面はInertiaのまま併用できます。Web画面自体を別アプリとして開発・配信する必要がなければ、Inertiaで画面用APIやCORS設定を省けます。2つ目は、既にBladeで動いている画面がどれだけあるかです。大半がBladeで、Reactにしたいのが検索フォームやグラフなど数か所なら、部分導入で足ります。

Reactスターターキットで始める手順とバージョン

laravel newでの作成と起動コマンド

PHP 8.3以上とComposerのほか、Node.jsが必要です。スターターキットのビルドに使うvite-plus 0.3.0は、package.json の engines で Node.js ^20.19.0 || ^22.18.0 || >=24.11.0 を指定しています。

公式ドキュメント(Starter Kits 13.x)の手順は、Laravelインストーラーを入れて laravel new を実行し、対話でスターターキットにReactを選ぶ流れです。認証の選択肢は、Laravel組み込みの認証(Fortify)とWorkOS AuthKitの2つです。

composer global require laravel/installer
laravel new my-app
cd my-app
npm install && npm run build
composer run dev

composer run dev の中身は php artisan dev で、php artisan dev:list で見ると、開発サーバー・キューワーカー・ログ表示(Pail)・Viteの4プロセスを同時に起動します。ブラウザで http://localhost:8000 を開けば、ログイン・登録・パスワード再設定・二要素認証・パスキーまで入った状態から始められます。

composer create-projectで直接作るとv1.0.1が入る落とし穴

インストーラーを使わず composer create-project laravel/react-starter-kit my-app と打つと、Composerは安定版タグの v1.0.1 を選びます。このタグは2025年2月24日のもので、PHPUnit 11やZiggyを使っていた頃の構成です。スターターキットは現在タグを切らずにmainブランチで更新されているため、現行版はdev扱いになっています。

インストーラー(laravel/installer v5.32.0)のソースを読むと、スターターキットの作成時に --stability=dev を付けています。手でComposerを叩く場合も同じオプションを付けてください。

composer create-project laravel/react-starter-kit my-app --stability=dev

この方法で作ると、作成直後に走る php artisan install:features が composer コマンドを呼びます。composer.phar だけを置いていてPATHに composer が無い環境では、ここが終了コード127で止まり、APP_KEY の生成とマイグレーションが実行されません。その場合は php artisan key:generate と php artisan migrate を手で実行します。

同梱パッケージのバージョン(2026年9月28日時点)

mainブランチ(コミット717b8f5)から作成した環境で、実際に入った版は次のとおりです。スターターキットの composer.json と package.json は ^ 付きの範囲指定なので、作成する日によって版は上がります。

パッケージ 入った版 役割
laravel/framework 13.33.0 本体
inertiajs/inertia-laravel 3.4.0 サーバー側アダプター
@inertiajs/react 3.7.1 クライアント側アダプター
laravel/fortify 1.40.0 認証バックエンド
react 19.3.0 UI
vite / vite-plus 8.3.1 / 0.3.0 ビルド
tailwindcss 4.3.3 CSS
typescript 5.9.3 型チェック

Laravel 13のサポート期限はLaravelの最新バージョンは13|対応PHPバージョン・サポート期限一覧とアップグレード判断にまとめています。注意したいのは、このスターターキットにSanctumも routes/api.php も含まれていない点です。APIが必要になったら後述の install:api で足します。

スターターキットのディレクトリ構成と各ファイルの役割

「LaravelとReactのファイルをどこに置くか」は、スターターキットの配置に合わせるのがいちばん揉めません。フロントのコードはすべて resources/js の下に入ります。

app/Http/Middleware/HandleInertiaRequests.php  # 全ページ共通のprops
config/inertia.php                             # SSRの接続先など
routes/web.php, routes/settings.php           # 画面のルート
resources/views/app.blade.php                 # 唯一のBlade(@viteReactRefresh・@vite)
resources/js/
├── app.tsx          # createInertiaApp とレイアウトの割り当て
├── pages/           # Inertia::render の名前と1対1のページ
├── components/      # 再利用部品(components/ui は shadcn/ui)
├── layouts/         # app・auth・settings のレイアウト
├── hooks/           # 自作フック
├── lib/             # ユーティリティ
├── types/           # 型定義
├── actions/, routes/, wayfinder/  # Wayfinderの生成物

運用で守るべきことは3つあります。まず、pages/ のファイルパスは Inertia::render('posts/index') の名前とそのまま対応するので、URLの階層に合わせて切ります。次に、actions/・routes/・wayfinder/ はLaravelのルート定義からWayfinderが生成する型付きのURL関数なので手で編集しません(vite.config.ts のlint対象からも外されています)。最後に、Fortifyの機能(メール認証など)を無効にするときは、resources/js 側の参照も消します。公式ドキュメントのとおり、消し忘れると生成されないルートを参照してビルドが失敗します。

InertiaでLaravelからReactへデータを渡す仕組み

Inertia::renderとpropsの書き方

Inertia構成では、コントローラーがJSONのAPIを返す代わりに、ページ名とpropsを返します。スターターキットに次のルートとページを足すと、/posts に一覧が表示されます。

// routes/web.php
use Inertia\Inertia;

Route::get('/posts', function () {
    return Inertia::render('posts/index', [
        'posts' => [
            ['id' => 1, 'title' => 'Hello'],
            ['id' => 2, 'title' => 'World'],
        ],
    ]);
});
// resources/js/pages/posts/index.tsx
type Post = { id: number; title: string };

export default function Index({ posts }: { posts: Post[] }) {
    return (
        <ul>
            {posts.map((post) => (
                <li key={post.id}>{post.title}</li>
            ))}
        </ul>
    );
}

ルートにデータを渡さない画面なら、スターターキットの routes/web.php と同じく Route::inertia('dashboard', 'dashboard') の1行で済みます。

初回HTMLと2回目以降のJSON応答

上の /posts に curl でリクエストすると、Inertiaの動き方がそのまま見えます。

リクエスト ステータス 返るもの
通常のGET(初回表示) 200 HTML+data-page のJSON
X-Inertia: true+一致するX-Inertia-Version 200 JSONのみ
X-Inertia: true(版なし・不一致) 409 X-Inertia-Location ヘッダー
一致する版のInertiaリクエスト+Partial-ComponentとPartial-Data 200 posts と errors だけ

部分リロードの行は、X-Inertia: true・一致する X-Inertia-Version・X-Inertia-Partial-Component: posts/index・X-Inertia-Partial-Data: posts をまとめて送った結果です。

初回はHTMLの中の <script data-page="app" type="application/json"> にページ名とpropsが埋め込まれ、以降の画面遷移ではJSONだけがやり取りされます。409は、デプロイでビルド成果物のハッシュが変わったときにブラウザ側で全体を読み直させるための応答です。propsには HandleInertiaRequests::share() で定義した共通値(スターターキットでは name・auth.user・sidebarOpen)が通常のページ応答に含まれます。ただし、部分リロードでは対象外の共有値を省略できます。ログインユーザーの全カラムをそのまま載せず、共有する項目は絞ってください。Inertia 3の変更点と仕組みの詳細はLaravel Inertiaとは|APIを作らずSPAを組む仕組みと3系の変更点で扱っています。

別アプリのReact(Next.js・Vite SPA)とSanctumで組むAPI分離構成

Laravel 13のインストールガイドは、Next.jsのようなJavaScriptアプリのAPIバックエンドとしてLaravelを使い、認証にSanctumを使う構成を案内しています。なお、公式のNext.js用ひな形だった laravel/breeze-next は2026年6月4日にアーカイブされ、現在は公式のNext.js向けスターターキットがありません。フロント側は自分で組む前提です。

install:apiで追加されるものと追加されないもの

スターターキットに php artisan install:api を実行すると、次のものが追加されます。

  • routes/api.php(auth:sanctum 付きの /user ルートだけが入った状態)
  • config/sanctum.php と personal_access_tokens テーブルのマイグレーション
  • laravel/sanctum(2026年9月28日時点で v4.3.3)

一方で、SPAのCookie認証に必要な statefulApi() は追加されません。config/cors.php も既定では存在しないので、php artisan config:publish cors で書き出してから編集します(書き出した直後の supports_credentials は false です)。API全体の設計はLaravel APIの作り方|install:apiとAPIリソース・エラー応答の統一を参照してください。

SPAのCookie認証を通す設定

Sanctumの公式ドキュメントでは、SPAとAPIが同じトップレベルドメインを共有することが前提です(app.example.com と api.example.com のようなサブドメイン違いは可)。そのうえで、次の4か所を設定します。

// bootstrap/app.php
->withMiddleware(function (Middleware $middleware): void {
    $middleware->statefulApi();
})

# .env
SANCTUM_STATEFUL_DOMAINS=app.example.com
SESSION_DOMAIN=.example.com

// config/cors.php
'paths' => ['api/*', 'sanctum/csrf-cookie', 'login'],
'allowed_origins' => ['https://app.example.com'],
'supports_credentials' => true,

config/cors.php の既定の paths は api/* と sanctum/csrf-cookie だけなので、別オリジンから呼ぶ login(Fortifyなら logout なども)を足します。React側では、ログインの前に /sanctum/csrf-cookie を呼んでCSRF用のCookieを受け取ります。Axiosなら withCredentials と withXSRFToken を有効にします。

import axios from 'axios';

axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.withCredentials = true;
axios.defaults.withXSRFToken = true;

export async function login(email: string, password: string) {
    await axios.get('/sanctum/csrf-cookie');
    await axios.post('/login', { email, password });
    const { data } = await axios.get('/api/user');
    return data;
}

/login はSanctumが用意するルートではありません。スターターキットならFortifyのルートがそのまま使え、API専用のLaravelなら自分でログイン用のルートを書きます。SANCTUM_STATEFUL_DOMAINS を指定しない場合の既定値には localhost・localhost:3000・127.0.0.1:8000 と APP_URL のホストが入っているので、ローカルのNext.js(ポート3000)はそのまま通ります。

statefulApiの有無で変わる応答

スターターキットに install:api を入れ、Originを http://localhost:3000 にしてCookieを保持したまま上の3リクエストを順に送ると、結果は次のように分かれます。

条件 csrf-cookie login /api/user
statefulApi なし 204 200 401
statefulApi あり 204 200 200
statefulApi あり・stateful外のOrigin 204 200 401

ログインは成功しているのにAPIだけが Unauthenticated. を返す場合があります。その場合は、ログイン処理よりも先に statefulApi() の有無と、フロントのドメインが SANCTUM_STATEFUL_DOMAINS に入っているかを確認してください。

トークン認証とPassportを選ぶ場面

SanctumのSPA用Cookie認証は、共通の親ドメインを持つWebフロントが対象です。自社SPAは同じ親ドメイン配下に配置する構成を優先し、以下のトークン認証はスマホアプリなどを対象にします。その場合はUserモデルに HasApiTokens を付け、createToken() で発行したトークンを Authorization: Bearer ヘッダーで送ります。Passportは、OAuth2の認可フローが必要な場合に選びます。第三者アプリへの権限委譲に加え、クライアントクレデンシャルによるマシン間認証なども対象です。自社のReact画面にログインさせる目的でPassportを入れると、使わないOAuthクライアント管理まで抱えることになります。Cookieとトークンの使い分けはLaravel Sanctumとは:SPAとモバイルのトークン認証とCookieセッションの使い分けで詳しく比較しています。

既存のBladeアプリにReactを部分導入する手順

画面の大半をBladeのまま残し、いいねボタンや検索フォームだけをReactにする構成です。以下は、コントローラーからBladeへ $post を渡し、postId と count を受け取る LikeButton コンポーネントを用意済みという前提の例です。Laravelの公式ドキュメント(Vite 13.x)の手順に沿って、プラグインとReactを入れます。

npm install --save-dev @vitejs/plugin-react
npm install react react-dom
// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';

export default defineConfig({
    plugins: [
        laravel(['resources/js/app.jsx']),
        react(),
    ],
});

Bladeのレイアウトでは、@viteReactRefresh を @vite より前に書きます(公式ドキュメントがこの順序を指定しています)。Reactに渡す値は json_encode() でJSONにし、{{ }} でダブルクォートの属性に出力します。{{ }} が引用符や < をHTMLエスケープするので、値にタグが含まれても属性は壊れません。@json([...]) で配列の中にカンマを書くと、Bladeがカンマを引数区切りと解釈し、エスケープ指定の外れた json_encode([...], 512) にコンパイルされる点に注意してください(Laravel 13.33.0で確認)。

{{-- resources/views/layouts/app.blade.php --}}
@viteReactRefresh
@vite('resources/js/app.jsx')

{{-- 埋め込みたい場所 --}}
<div id="like-button" data-props="{{ json_encode(['postId' => $post->id, 'count' => $post->likes_count]) }}"></div>
// resources/js/app.jsx
import { createRoot } from 'react-dom/client';
import LikeButton from './components/LikeButton';

const el = document.getElementById('like-button');
if (el) {
    createRoot(el).render(<LikeButton {...JSON.parse(el.dataset.props)} />);
}

ファイル拡張子は .jsx か .tsx にし、設定後は npm run dev で開発サーバーを起動するか npm run build でビルドします。Reactの部品が複数ページに広がり、ページ遷移まで自前で書き始めたら、Inertiaへ移るタイミングです。@vite の書き方やビルド時の注意はLaravel Viteの使い方|@viteの書き方と本番ビルドの落とし穴にまとめています。

SSRと本番デプロイで必要な設定

Inertia SSRの有効化と起動コマンド

InertiaのSSRを使うのに、Next.jsは要りません。スターターキットの config/inertia.php は最初から 'ssr' => ['enabled' => true, 'url' => 'http://127.0.0.1:13714'] になっていて、SSR用のバンドルを作ってNodeのSSRサーバーを起動するだけで有効になります。

npm run build:ssr              # bootstrap/ssr/app.js を生成
php artisan inertia:start-ssr  # 13714番ポートでSSRサーバーを起動
php artisan inertia:check-ssr  # "Inertia SSR server is running." を確認

SSRサーバーを動かした状態で /posts を取得すると、サイドバーや一覧の <li> がサーバー側で描画済みのHTMLとして返ります。公式ドキュメント(Starter Kits 13.x)には composer dev:ssr というコマンドも載っていますが、2026年9月28日時点のmainブランチの composer.json にはこのスクリプトがありません。上の2つのコマンドを使ってください。

本番環境へのデプロイ手順

Inertia構成でも部分導入でも、サーバーに置くのは1つのLaravelアプリです。ビルドはデプロイ時に1回だけ行い、public/build を配信します。

composer install --no-dev --optimize-autoloader
npm ci
npm run build          # SSRを使うなら npm run build:ssr
php artisan migrate --force
php artisan optimize

SSRを使う場合は、php artisan inertia:start-ssr をSupervisorやsystemdで常駐させ、デプロイのたびに再起動します。ビルドのハッシュが変わると、開いたままのブラウザには前述の409が返って自動で再読み込みされるので、利用者に手動での更新を求める必要はありません。API分離構成では、LaravelとReactを別々にデプロイし、Laravel側の SANCTUM_STATEFUL_DOMAINS と SESSION_DOMAIN を本番のドメインに書き換えます。

React連携を採用しない方がよい場面と失敗パターン

React連携は、条件が合わない案件に入れると保守費用だけが増えます。次のどれかに当てはまるなら、構成を見直してください。

  • PHPしか書けないチームの管理画面:Reactの依存更新(React・Vite・Tailwind・shadcn/ui)を追い続ける人がいないなら、BladeかLivewireを選びます。
  • 検索流入が要る公開ページをSSRなしのInertiaで作る:初回HTMLに入るのはpropsのJSONで、本文はJavaScriptの実行後に描画されます。公開ページはSSRを有効にするか、Bladeで書きます。
  • SPAとAPIのドメインが別(例:shop-a.com と api.shop-b.net):SanctumのCookie認証は使えません。自社SPAでは、APIをSPAと同じ親ドメイン配下に置く構成を優先します。別ドメインが必須なら、認証方式とトークンの保持場所を別途設計します。
  • 「将来モバイルアプリを作るかも」という理由だけでAPI分離にする:API・CORS・認証を最初から2系統持つことになります。Inertiaで始めても、必要になった時点で install:api でAPIを追加できます。

よくある質問

LaravelとReactはどちらを選ぶべきですか?

どちらか一方を選ぶものではありません。LaravelはPHPでデータベース・認証・ルーティングを扱うサーバー側のフレームワーク、ReactはブラウザのUIを組むライブラリで、両方を組み合わせて使います。実際に決めるのは「Laravelの画面をBlade・Livewire・React・Vueのどれで書くか」で、JavaScriptを書く人がいてUI部品が多いならReact、PHP中心のチームならLivewireが扱いやすい選択です。

LaravelのフロントエンドはReactとVueのどちらがよいですか?

Laravel側の差はほぼありません。公式のReact・Vueスターターキットはどちらも Inertia 3・TypeScript・Tailwind・shadcn系のUIで構成され、コントローラーやルートの書き方は同じです。決め手はチームの経験と使いたいライブラリです。React向けのUIライブラリ(shadcn/uiなど)をそのまま使いたいならReact、テンプレート記法に慣れた人が多いならVueを選びます。

Laravel BladeとReactはどう使い分けますか?

ページ全体を描き直して困らない画面はBlade、入力に応じて画面の一部が頻繁に変わる画面はReactが向きます。混在するなら、Viteに @vitejs/plugin-react を足し、Bladeで @viteReactRefresh を @vite より前に書けば、既存画面を残したまま数か所だけReactにできます。

LaravelでReactのSPAを作るにはAPIが必要ですか?

Inertiaを使うなら不要です。コントローラーが Inertia::render() でページ名とpropsを返し、画面遷移時はInertiaがJSONだけを取得します。APIが必要になるのは、Next.jsなど別アプリのReactやスマホアプリが同じデータを読む場合で、php artisan install:api でSanctumを入れ、SPAのCookie認証なら statefulApi() も追加します。

Laravel BreezeやReactプリセットは今も使えますか?

laravel/breeze の v2.4.2(2026年5月19日)はLaravel 13へのインストールを許容していますが、Laravel 13の公式ドキュメントが新規作成で案内しているのは新しいReactスターターキットです。laravel/ui の php artisan ui react で入るReactプリセットも、Inertiaを使わない旧来の構成です。これから作るなら laravel new でReactスターターキットを選んでください。既存のBreeze環境の移行はLaravel Breeze・Jetstreamはなぜ非推奨に?で解説しています。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.07.21 テックブログ Apache Tomcatの脆弱性一覧【2026年10月】最新版11.0.27・10.1.61・9.0.123とCVE-2026-59083ほかの影響確認・対応手順
  2. 2026.10.22 テックブログ Tomcatバージョン一覧とサポート期限【2026年10月】9.0系は2027年3月末で終了・9.1系と11.0系への移行判断
  3. 2026.10.21 テックブログ NEC委託先の不正アクセスとは|顧客システム暗号化・情報漏えいの経緯と委託元が点検する正規アカウント
  4. 2026.10.21 テックブログ ふるさとチョイス不正アクセスで445万件流出:対象確認・パスワード変更と再開時期
  5. 2026.10.23 テックブログ Renta!不正アクセスでサービス停止:再開未定の現状と運営側の停止判断・証拠保全・段階的再開

RELATED POSTS 関連記事

目次