yii3/inertia
| Install | |
|---|---|
composer require yii3/inertia |
|
| PHP: | >=8.3 |
| License: | BSD-3-Clause |
| Last Updated: | Sep 11, 2026 |
| Links: | GitHub · Packagist |
Server-side Inertia.js v3 integration for Yii3. The package uses constructor injection, PSR-7 responses, PSR-15 middleware, and Yii Config Plugin configuration. It does not expose a static facade or read from a service locator.
Architecture
The packages have deliberately separate responsibilities:
php-forge/inertiaimplements the framework-agnostic protocol, page model, prop resolution, headers, redirects, and result objects.yii3/inertiaadapts Yii3 request, response, session, and view services to that core.
Asset emission is an application concern. The adapter renders no script or link tags of its own; the default root view
marks Yii's head and body placeholders, so whatever the application registers on Yiisoft\View\WebView reaches the
initial document. React, Vue, and the build tool remain application choices, and this adapter ships no
framework-specific JavaScript packages.
Requirements
- PHP 8.3 or later.
- A Yii3 application with PSR-17 response and stream factories.
yiisoft/sessionandyiisoft/csrffor flash data, validation errors, and the XSRF cookie flow.yiisoft/request-body-parserfor JSON form submissions.yiisoft/viewfor rendering the initial HTML document through the application web view.php-forge/inertiafor the framework-neutral Inertia protocol and prop types.
Installation
Applications should declare the adapter, the native PHP Forge packages they use, and Yii's request body parser as direct dependencies:
composer require yii3/inertia:^0.1 php-forge/inertia:^0.5 yiisoft/request-body-parser:^1.2
For a local sibling checkout, add a Composer path repository:
{
"repositories": [
{
"type": "path",
"url": "../inertia",
"options": {
"symlink": true,
"reference": "config"
}
}
],
"require": {
"yii3/inertia": "dev-main",
"php-forge/inertia": "^0.5",
"yiisoft/request-body-parser": "^1.2"
}
}
The Yii Config Plugin merges config/params.php and the web-only config/di-web.php automatically.
Middleware order
Place the middleware around the Yii3 web stack in this order:
use Yii3\Inertia\Middleware\CsrfTokenCookieMiddleware;
use Yii3\Inertia\Middleware\InertiaMiddleware;
use Yiisoft\Csrf\CsrfTokenMiddleware;
use Yiisoft\ErrorHandler\Middleware\ErrorCatcher;
use Yiisoft\Request\Body\RequestBodyParser;
use Yiisoft\RequestProvider\RequestCatcherMiddleware;
use Yiisoft\Router\Middleware\Router;
use Yiisoft\Session\SessionMiddleware;
return [
InertiaMiddleware::class,
ErrorCatcher::class,
SessionMiddleware::class,
RequestBodyParser::class,
CsrfTokenCookieMiddleware::class,
CsrfTokenMiddleware::class,
RequestCatcherMiddleware::class,
Router::class,
];
This order ensures that:
- Inertia headers are added to normal and error responses.
- JSON form bodies are available before CSRF validation.
- The session is open when the readable
XSRF-TOKENcookie is generated. - Mutable shared props are reset before and after every request, including failed requests.
The package configures Yii's CSRF validator to accept X-XSRF-TOKEN. Do not encrypt or sign the XSRF-TOKEN
cookie with CookieMiddleware; the browser client must be able to read and return the masked token.
Configuration
Override the yii3/inertia parameter tree in application configuration:
<?php
declare(strict_types=1);
$manifest = dirname(__DIR__, 2) . '/public/build/.vite/manifest.json';
return [
'yii3/inertia' => [
'title' => 'My application',
'version' => static function () use ($manifest): string|null {
if (!is_file($manifest)) {
return null;
}
$hash = hash_file('xxh128', $manifest);
return $hash === false ? null : $hash;
},
'shared' => [
'application' => ['name' => 'My application'],
],
'csrf' => [
// null enables HTTPS auto-detection. Use true only when trusted-proxy
// middleware does not normalize the request URI scheme.
'secure' => null,
],
],
];
The full parameter tree contains:
id,rootView,language,charset, andtitlefor the initial document.version,shared, anderrorFlashKeyfor page construction.csrf.cookieName,headerName,parameterName,path,domain,secure, andsameSite.
Configurable services keep constructors to at most four dependencies. Yii's DI definitions apply package parameters
through immutable with*() methods, so each configured instance is cloned instead of mutated.
The configured root-view alias is resolved with Yiisoft\Aliases\Aliases and rendered by the application's
Yiisoft\View\WebView. Custom root views therefore use Yii's configured renderers, themes, common parameters, and
render events instead of a package-owned PHP file loader.
Assets
The adapter emits no asset tags. The default root view marks Yii's head and body placeholders, so anything registered
on the application Yiisoft\View\WebView before the response is rendered reaches the initial document:
$view->registerCssFile('/build/app.css');
$view->registerJsFile('/build/app.js', options: ['type' => 'module']);
$view->registerLink(['rel' => 'modulepreload', 'href' => '/build/vendor.js']);
Stylesheets and links land in <head>; scripts land at the end of <body> unless another position is requested.
yiisoft/assets bundles work the same way through WebView::addCssFiles() and WebView::addJsFiles().
Vite
Install php-forge/vite when the application uses Vite:
composer require php-forge/vite:^0.5
The application owns its Vite mode and entrypoints. Define the native PHPForge\Vite\Vite service directly.
Production example:
<?php
declare(strict_types=1);
use PHPForge\Vite\Configuration\ProductionConfiguration;
use PHPForge\Vite\Vite;
return [
Vite::class => static fn(): Vite => Vite::create(
ProductionConfiguration::create(
manifestPath: dirname(__DIR__, 2) . '/public/build/.vite/manifest.json',
assetBaseUrl: '/build',
),
entrypoints: ['resources/js/app.ts'],
),
];
During development, register the native development service:
<?php
declare(strict_types=1);
use PHPForge\Vite\Configuration\DevelopmentConfiguration;
use PHPForge\Vite\Vite;
return [
Vite::class => static fn(): Vite => Vite::create(
DevelopmentConfiguration::create('http://localhost:5173'),
entrypoints: ['resources/js/app.ts'],
),
];
React applications may pass an application-owned InlineModuleProviderInterface implementation to
DevelopmentConfiguration::create() for the React Refresh preamble. Vue applications do not need a preamble.
Render the resolved assets from an application-owned root view. Hand the service to the view through
WebView::setParameter() during bootstrap, or through the $viewData argument of Inertia::render():
<?php
declare(strict_types=1);
use PHPForge\Vite\Html\HtmlRenderer;
use PHPForge\Vite\Vite;
use Yiisoft\View\WebView;
/**
* @var string $charset
* @var string $id
* @var string $language
* @var string $pageJson
* @var string $title
* @var Vite $vite
* @var WebView $this
*/
$encode = static fn(string $value): string => htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
$this->beginPage();
?>
<!DOCTYPE html>
<html lang="<?= $encode($language) ?>">
<head>
<meta charset="<?= $encode($charset) ?>">
<title data-inertia><?= $encode($title) ?></title>
<?php $this->head() ?>
<?= HtmlRenderer::create()->render($vite->resolve()) ?>
</head>
<body>
<?php $this->beginBody() ?>
<script data-page="<?= $encode($id) ?>" type="application/json"><?= $pageJson ?></script>
<div id="<?= $encode($id) ?>"></div>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage();
Point the rootView parameter at that file. Custom root views must call beginPage() and endPage(); the head and
body placeholders are only substituted between those calls.
Rendering pages
Inject Yii3\Inertia\Inertia into an action and return its PSR-7 response. Use the native PHP Forge prop factories;
this package does not duplicate them.
use PHPForge\Inertia\Prop\Prop;
use PHPForge\Inertia\Prop\ScrollMetadata;
use Psr\Http\Message\ResponseInterface;
use Yii3\Inertia\Inertia;
final readonly class DashboardAction
{
public function __construct(private Inertia $inertia) {}
public function __invoke(): ResponseInterface
{
$this->inertia->share('auth.user', ['id' => 42, 'name' => 'Ada']);
return $this->inertia->render('Dashboard', [
'summary' => static fn(): array => ['projects' => 12],
'activity' => Prop::defer(static fn(): array => loadActivity(), 'dashboard', rescue: true),
'audit' => Prop::optional(static fn(): array => loadAudit())->once(),
'permissions' => Prop::always(['projects.read']),
'users' => Prop::merge(loadUsers())->append('data', matchOn: 'id'),
'messages' => Prop::merge(loadMessages())->prepend(),
'settings' => Prop::merge(loadSettings())->deepMerge(),
'countries' => Prop::once(static fn(): array => loadCountries())
->as('country-list')
->until(3600),
'feed' => Prop::scroll(
loadFeed(),
new ScrollMetadata('page', previousPage: null, nextPage: 2, currentPage: 1),
),
]);
}
}
Plain page, shared, and version closures are invoked without arguments, matching the PHP Forge core contract. Resolve request-dependent values explicitly in the action or capture the request in a zero-argument closure. Scroll metadata closures are the exception: the core passes them the resolved scroll value.
Page props replace shared props at the top-level key, matching the official adapter behavior. The response exposes the
top-level shared keys through sharedProps, allowing Inertia v3 instant visits to retain shared application data.
Session flash data is emitted only in the page-level flash field so it cannot replay from browser-history props.
Public API
Yii3\Inertia\Inertia exposes:
render(),location(),isInertiaRequest(),getVersion(), andnormalizeResponse().share(),getShared(),flushShared(), andreset().- Immutable
with*()methods for service configuration.
All page and prop value objects come directly from php-forge/inertia. The adapter owns no asset abstraction: tags
are registered on Yiisoft\View\WebView or rendered by an application-owned root view.
Adapter-owned exception text is centralized in Yii3\Inertia\Exception\Message. Exceptions from
php-forge/inertia, Yii View, and other dependencies retain their native types and messages.
Debug and telemetry packages may implement ResolvedPageObserverInterface. The observer receives the resolved core
Page synchronously and must keep captured request data request-scoped. Observation is skipped when no implementation
is bound.
See the protocol notes for header and payload details.
Documentation
For detailed configuration options and advanced usage.
Package information
Project status
Community
License
Resolved-page observation
PHPForge\Inertia\ResolvedPageObserver forwards the resolved page payload and shared-prop keys to a callback.
Observer failures propagate to the caller; the observer does not mutate pages or hide callback failures.
Yii3 exposes the named Yii3\Inertia\ResolvedPageObserver, which implements the existing
ResolvedPageObserverInterface. Pass it to withPageObserver() or register it under that interface in DI.
Existing observers and signatures remain supported. This integration requires the core 0.3 development line.
Related Packages
Yii2 adapter for the framework-agnostic Inertia.js PHP protocol core.
Laravel Localizer bridges Laravel translations to your SPA frontend (React/Vue/I...