Inertia.js для PHP: полное руководство с примерами
1. Что это и зачем нужна
Inertia.js — это не фреймворк и не библиотека для создания SPA (Single Page Application) в классическом понимании. Это скорее «мост» или «адаптер», который позволяет создавать современные одностраничные приложения, используя классический серверный фреймворк (в нашем случае Laravel на PHP) и любой современный фронтенд-фреймворк (React, Vue.js или Svelte) без необходимости писать отдельный REST API.
Главная проблема, которую решает Inertia.js — это отказ от традиционного подхода «сервер рендерит HTML» или «сервер отдаёт JSON, а фронтенд сам решает, что рисовать». Вместо этого Inertia предлагает третий путь: вы пишете контроллеры на PHP как обычно, но вместо возврата Blade-шаблона или JSON-ответа, вы возвращаете Inertia-ответ с данными и именем компонента. Inertia на клиенте (JavaScript) перехватывает этот ответ, находит нужный компонент (React/Vue) и рендерит его, подставляя полученные данные. Навигация между страницами происходит без полной перезагрузки, как в SPA, но весь роутинг и бизнес-логика остаются на сервере.
Зачем это нужно? Во-первых, это резко упрощает архитектуру: вам не нужно поддерживать два отдельных приложения (бэкенд API и фронтенд SPA) с дублированием логики авторизации и валидации. Во-вторых, это сохраняет все преимущества Laravel (ORM Eloquent, Blade-компоненты для писем, Artisan, middleware) и все преимущества React/Vue (реактивность, компонентный подход, огромная экосистема). В-третьих, это даёт отличный Developer Experience (DX): вы работаете в одном репозитории, используете один роутер (Laravel), а Inertia прозрачно управляет состоянием приложения на клиенте.
2. Установка
Установка состоит из двух частей: серверной (PHP) и клиентской (JavaScript). Предполагается, что у вас уже установлен Laravel (версия 8, 9, 10 или 11).
Шаг 1: Установка серверного пакета (PHP)
Выполните в терминале в корне вашего Laravel-проекта:
composer require inertiajs/inertia-laravel
Шаг 2: Установка клиентского адаптера (JavaScript)
Выберите ваш фронтенд-фреймворк и выполните соответствующую команду:
Для React:
npm install @inertiajs/react react react-dom
Для Vue 3:
npm install @inertiajs/vue3 vue
Для Svelte:
npm install @inertiajs/svelte svelte
Шаг 3: Публикация конфигурации (опционально)
php artisan vendor:publish --provider="Inertia\ServiceProvider"
Шаг 4: Настройка корневого шаблона
Создайте файл resources/views/app.blade.php со следующим содержимым:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', config('app.name'))</title>
@viteReactRefresh
@vite(['resources/js/app.jsx'])
@inertiaHead
</head>
<body>
@inertia
</body>
</html>
Директива @inertia — это место, куда Inertia будет вставлять ваше приложение. @inertiaHead — для управления заголовками из компонентов.
Шаг 5: Настройка Vite (если используете Vite)
В файле vite.config.js добавьте плагин для React или Vue:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react'; // или @vitejs/plugin-vue
export default defineConfig({
plugins: [
laravel({
input: 'resources/js/app.jsx',
refresh: true,
}),
react(), // или vue()
],
});
3. Быстрый старт — минимальный рабочий пример
Создадим простую страницу «О нас».
Шаг 1: Контроллер
Создайте контроллер app/Http/Controllers/PageController.php:
<?php
namespace App\Http\Controllers;
use Inertia\Inertia;
class PageController extends Controller
{
public function about()
{
return Inertia::render('About', [
'companyName' => 'PythonLib',
'year' => date('Y'),
]);
}
}
Шаг 2: Роут
Добавьте в routes/web.php:
use App\Http\Controllers\PageController;
Route::get('/about', [PageController::class, 'about']);
Шаг 3: Компонент React
Создайте файл resources/js/Pages/About.jsx:
import React from 'react';
import { Head } from '@inertiajs/react';
export default function About({ companyName, year }) {
return (
<>
<Head title="О нас" />
<div className="container mx-auto p-8">
<h1 className="text-3xl font-bold">О компании {companyName}</h1>
<p className="mt-4 text-gray-600">Год основания: {year}</p>
</div>
</>
);
}
Шаг 4: Точка входа
Файл resources/js/app.jsx:
import React from 'react';
import { createInertiaApp } from '@inertiajs/react';
import { createRoot } from 'react-dom/client';
createInertiaApp({
resolve: name => {
const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true });
return pages[`./Pages/${name}.jsx`];
},
setup({ el, App, props }) {
createRoot(el).render(<App {...props} />);
},
});
Теперь перейдите по адресу /about — вы увидите страницу, отрендеренную React, но данные пришли из PHP-контроллера.
4. Основные методы/функции/классы
4.1. Inertia::render()
Сигнатура: Inertia::render(string $component, array $props = []): \Inertia\Response
Что делает: Основной метод для возврата Inertia-ответа. Принимает имя компонента (без расширения) и массив данных (props), которые будут переданы на фронтенд.
Пример:
return Inertia::render('Users/Index', [
'users' => User::all(),
'filters' => request()->only(['search', 'role']),
]);
4.2. Inertia::location()
Сигнатура: Inertia::location(string $url): \Symfony\Component\HttpFoundation\RedirectResponse
Что делает: Выполняет полную переадресацию браузера (не SPA-навигацию). Используется для редиректов на внешние сайты или для выхода из приложения Inertia (например, при logout).
Пример:
return Inertia::location('https://example.com/external-page');
4.3. Inertia::share()
Сигнатура: Inertia::share(string|array $key, mixed $value = null): void
Что делает: Позволяет передавать данные (например, текущего пользователя, настройки) во все компоненты глобально, без необходимости передавать их в каждом render().
Пример (в AppServiceProvider::boot()):
Inertia::share('auth.user', function () {
return auth()->user() ? [
'id' => auth()->user()->id,
'name' => auth()->user()->name,
'email' => auth()->user()->email,
] : null;
});
// Или массивом:
Inertia::share([
'appName' => config('app.name'),
'flash' => function () {
return [
'success' => session('success'),
'error' => session('error'),
];
},
]);
4.4. Inertia::getShared()
Сигнатура: Inertia::getShared(string|null $key = null): mixed
Что делает: Позволяет получить ранее переданные через share() глобальные данные. Если ключ не указан, возвращает все данные.
Пример:
$user = Inertia::getShared('auth.user');
$allShared = Inertia::getShared();
4.5. Inertia::lazy()
Сигнатура: Inertia::lazy(\Closure|callable $callback): \Inertia\LazyProp
Что делает: Позволяет отложить загрузку «тяжёлых» данных. Данные будут запрошены только тогда, когда компонент их явно запросит (через специальный запрос на сервер). Критически важно для производительности больших страниц.
Пример:
return Inertia::render('Dashboard', [
'userCount' => User::count(), // всегда загружается
'recentPosts' => Inertia::lazy(fn () => Post::latest()->take(10)->get()), // загружается лениво
]);
4.6. Inertia::always()
Сигнатура: Inertia::always(string $key, \Closure|callable $callback): void
Что делает: Похож на share(), но данные вычисляются каждый раз при запросе, а не кешируются. Полезно для данных, которые могут меняться между запросами (например, количество уведомлений).
Пример:
Inertia::always('notificationsCount', function () {
return auth()->user()?->unreadNotifications()->count() ?? 0;
});
4.7. Inertia::version()
Сигнатура: Inertia::version(string $version): void
Что делает: Устанавливает версию ассетов (CSS/JS). Когда версия меняется, Inertia автоматически выполняет полную перезагрузку страницы, чтобы избежать ошибок из-за несоответствия кода.
Пример (в AppServiceProvider):
Inertia::version(fn () => md5_file(public_path('mix-manifest.json'))); // для Laravel Mix
// или для Vite:
Inertia::version(fn () => filemtime(public_path('build/manifest.json')));
4.8. Inertia::getVersion()
Сигнатура: Inertia::getVersion(): string|null
Что делает: Возвращает текущую версию ассетов, установленную через version().
Пример:
$currentVersion = Inertia::getVersion(); // 'a1b2c3d4...'
5. Реальный практический пример (CRUD для постов блога)
Создадим полноценный CRUD для постов с пагинацией, поиском и flash-сообщениями.
Контроллер: app/Http/Controllers/PostController.php
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
use Inertia\Inertia;
class PostController extends Controller
{
public function index(Request $request)
{
$query = Post::query();
// Поиск по заголовку
if ($search = $request->input('search')) {
$query->where('title', 'like', "%{$search}%");
}
$posts = $query->orderBy('created_at', 'desc')
->paginate(10)
->withQueryString(); // сохраняем параметры запроса в пагинации
return Inertia::render('Posts/Index', [
'posts' => $posts,
'filters' => $request->only(['search']),
]);
}
public function create()
{
return Inertia::render('Posts/Create');
}