Search by

yii3 / inertia

terabytesoftw

Inertia.js v3 server-side integration for Yii3.

Package info

github.com/yii3/inertia

pkg:composer/yii3/inertia

Statistics

Installs: 609

Dependents: 1

Suggesters: 0

Stars: 2

Open Issues: 0

dev-main / 0.1.x-dev 2026-09-19 18:20 UTC

README

Yii Framework

Inertia for Yii3

PHPUnit Mutation Testing PHPStan Security

Connect Yii3 requests, responses, views, sessions, and redirects to the Inertia protocol

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, and exposes the prop factories an action needs, so application code names no core class.

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, installed by this package, for the framework-neutral Inertia protocol and prop types.

Installation

Applications declare the adapter and Yii's request body parser as direct dependencies; the adapter installs php-forge/inertia itself. Declare that core package too only when application code type-hints its prop or page classes.

composer require yii3/inertia:^0.1 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",
        "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/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().

Custom root view

Render assets from an application-owned root view. Hand values to the view through WebView::setParameter() during bootstrap, or through the $viewData argument of Inertia::render():

<?php

declare(strict_types=1);

use Yiisoft\View\WebView;

/**
 * @var string $charset
 * @var string $id
 * @var string $language
 * @var string $pageJson
 * @var string $title
 * @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() ?>
</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. The service also creates every prop kind, so an action imports nothing from the core:

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' => $this->inertia->defer(static fn(): array => loadActivity(), 'dashboard', rescue: true),
            'audit' => $this->inertia->optional(static fn(): array => loadAudit())->once(),
            'permissions' => $this->inertia->always(['projects.read']),
            'users' => $this->inertia->merge(loadUsers())->append('data', matchOn: 'id'),
            'messages' => $this->inertia->merge(loadMessages())->prepend(),
            'settings' => $this->inertia->deepMerge(loadSettings()),
            'countries' => $this->inertia->once(static fn(): array => loadCountries())
                ->as('country-list')
                ->until(3600),
            'feed' => $this->inertia->scroll(
                loadFeed(),
                $this->inertia->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().
  • The prop factories always(), defer(), merge(), deepMerge(), once(), optional(), scroll(), and scrollMetadata().
  • Immutable with*() methods for service configuration.

The prop factories return the prop objects of php-forge/inertia, so the fluent modifiers documented there apply to them; scrollMetadata() returns the immutable ScrollMetadata value that scroll() consumes. 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.