yii3/inertia

Inertia.js v3 server-side integration for Yii3.
466 2
Install
composer require yii3/inertia
PHP:>=8.3
License:BSD-3-Clause
Last Updated:Sep 11, 2026
Links: GitHub  ·  Packagist
Maintainer: terabytesoftw

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/inertia implements the framework-agnostic protocol, page model, prop resolution, headers, redirects, and result objects.
  • yii3/inertia adapts 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/session and yiisoft/csrf for flash data, validation errors, and the XSRF cookie flow.
  • yiisoft/request-body-parser for JSON form submissions.
  • yiisoft/view for rendering the initial HTML document through the application web view.
  • php-forge/inertia for 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-TOKEN cookie 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, and title for the initial document.
  • version, shared, and errorFlashKey for page construction.
  • csrf.cookieName, headerName, parameterName, path, domain, secure, and sameSite.

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(), and normalizeResponse().
  • share(), getShared(), flushShared(), and reset().
  • 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

PHP Yii3 Inertia.js 3

Project status

PHPStan Level Max Quality ECS Dependencies

Community

Follow on X Yii Forum Join on Telegram

License

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

crenspire/yii3-inertia

Inertia.js server-side adapter for Yii3 and any PSR-15 application

39 1
yii2-extensions/inertia

Yii2 adapter for the framework-agnostic Inertia.js PHP protocol core.

2,712 3
webkulwp/inertia

Inertia.js server-side adapter for PHP. Handles full visits, Inertia XHR visits,...

7 15