You are viewing revision #1 of this wiki article.
This is the latest version of this article.
- Introduction
- Installation
- Quick Setup
- Core Features
- Best Practices
- Common Patterns
- Troubleshooting
- Additional Resources
- Conclusion
Introduction ¶
Inertia.js is a modern approach to building single-page applications (SPAs) without the complexity of building an API. It allows you to use modern JavaScript frameworks like React, Vue, or Svelte while keeping your Yii2 controllers and routing intact.
Why Inertia.js?
- No need to build a separate API
- Keep your existing Yii2 controllers and models
- Server-side routing and validation
- Better SEO than traditional SPAs
- Simpler architecture than API + SPA
The crenspire/yii2-inertia package provides a seamless Inertia.js adapter for Yii2, matching the developer experience of popular frameworks like Laravel's Inertia adapter.
Installation ¶
Install via Composer:
composer require crenspire/yii2-inertia
Quick Setup ¶
1. Configure the View Renderer ¶
Add to your config/web.php:
'view' => [
'renderers' => [
'inertia' => \Crenspire\Yii2Inertia\ViewRenderer::class,
],
],
2. Create Root Template ¶
Create views/layouts/inertia.php:
<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= Html::encode($this->title) ?></title>
<script type="module" src="/dist/assets/index.js"></script>
<link rel="stylesheet" href="/dist/assets/index.css">
</head>
<body>
<div id="app" data-page="<?= htmlspecialchars(json_encode($page), ENT_QUOTES, 'UTF-8') ?>"></div>
</body>
</html>
3. Use in Controllers ¶
use Crenspire\Yii2Inertia\Inertia;
class HomeController extends \yii\web\Controller
{
public function actionIndex()
{
return Inertia::render('Home', [
'title' => 'Welcome',
'users' => User::find()->all(),
]);
}
}
4. Setup Frontend ¶
Install Inertia.js and your framework:
# For React
npm install @inertiajs/inertia @inertiajs/inertia-react react react-dom
# For Vue
npm install @inertiajs/inertia @inertiajs/inertia-vue3 vue
# For Svelte
npm install @inertiajs/inertia @inertiajs/inertia-svelte svelte
Create src/main.jsx (React example):
import React from 'react';
import ReactDOM from 'react-dom/client';
import { createInertiaApp } from '@inertiajs/inertia-react';
import Home from './pages/Home';
import Dashboard from './pages/Dashboard';
createInertiaApp({
resolve: (name) => {
const pages = { Home, Dashboard };
return pages[name];
},
setup({ el, App, props }) {
ReactDOM.createRoot(el).render(<App {...props} />);
},
});
Core Features ¶
Sharing Global Data ¶
Share data that should be available on every page:
// In config/bootstrap.php or base controller
use Crenspire\Yii2Inertia\Inertia;
// Share user data
Inertia::share('user', function () {
return Yii::$app->user->identity;
});
// Share flash messages
Inertia::share('flash', function () {
return [
'success' => Yii::$app->session->getFlash('success'),
'error' => Yii::$app->session->getFlash('error'),
];
});
// Share multiple values
Inertia::share([
'appName' => 'My Application',
'version' => '1.0.0',
]);
Tip: Use closures for dynamic data (like user or flash messages) to ensure fresh data on each request.
Handling Redirects ¶
For form submissions and other redirects, use Inertia::location():
public function actionStore()
{
$model = new User();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
Yii::$app->session->setFlash('success', 'User created!');
return Inertia::location('/users');
}
return Inertia::render('Users/Create', [
'errors' => $model->errors,
]);
}
The method automatically handles both Inertia requests (409 status) and regular requests (302 redirect).
Asset Versioning ¶
Set up versioning for cache busting:
// Automatic (uses manifest.json mtime if exists)
$version = Inertia::version();
// Manual string
Inertia::version('1.0.0');
// Callback (recommended)
Inertia::version(function () {
return filemtime(Yii::getAlias('@webroot/dist/manifest.json'));
});
Partial Reloads ¶
Optimize performance by only reloading specific props:
// Client requests only 'users' and 'stats' props
return Inertia::render('Dashboard', [
'users' => $users, // Included
'stats' => $stats, // Included
'other' => $other, // Excluded
]);
In your frontend component:
import { router } from '@inertiajs/inertia-react';
const refreshStats = () => {
router.reload({ only: ['stats'] });
};
Best Practices ¶
1. Organize Components by Route ¶
Structure your frontend to match backend routes:
src/pages/
Home.jsx
Dashboard.jsx
Users/
Index.jsx
Show.jsx
Edit.jsx
// Maps to src/pages/Users/Index.jsx
return Inertia::render('Users/Index', ['users' => $users]);
2. Handle Forms with Inertia ¶
Use Inertia's form helper for better UX:
import { useForm } from '@inertiajs/inertia-react';
export default function CreateUser() {
const { data, setData, post, processing, errors } = useForm({
name: '',
email: '',
});
const submit = (e) => {
e.preventDefault();
post('/users');
};
return (
<form onSubmit={submit}>
<input
value={data.name}
onChange={(e) => setData('name', e.target.value)}
/>
{errors.name && <div>{errors.name}</div>}
<button disabled={processing}>Submit</button>
</form>
);
}
3. Error Handling ¶
Return validation errors from your controller:
public function actionStore()
{
$model = new User();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return Inertia::location('/users');
}
// Return with errors
return Inertia::render('Users/Create', [
'errors' => $model->errors,
'values' => $model->attributes,
]);
}
4. Flash Messages ¶
Create a reusable flash message component:
// Share flash messages globally
Inertia::share('flash', function () {
return [
'success' => Yii::$app->session->getFlash('success'),
'error' => Yii::$app->session->getFlash('error'),
];
});
// FlashMessage.jsx
import { usePage } from '@inertiajs/inertia-react';
export default function FlashMessage() {
const { flash } = usePage().props;
if (!flash) return null;
return (
<div>
{flash.success && <div className="alert-success">{flash.success}</div>}
{flash.error && <div className="alert-error">{flash.error}</div>}
</div>
);
}
5. Pagination ¶
Handle pagination with Yii2's DataProvider:
use yii\data\ActiveDataProvider;
public function actionIndex()
{
$dataProvider = new ActiveDataProvider([
'query' => User::find(),
'pagination' => ['pageSize' => 15],
]);
return Inertia::render('Users/Index', [
'users' => $dataProvider->getModels(),
'pagination' => [
'currentPage' => $dataProvider->pagination->page + 1,
'lastPage' => $dataProvider->pagination->pageCount,
'perPage' => $dataProvider->pagination->pageSize,
'total' => $dataProvider->totalCount,
],
]);
}
6. Optimize Database Queries ¶
Use eager loading to prevent N+1 queries:
$users = User::find()
->with('posts', 'comments')
->all();
7. Minimize Prop Size ¶
Only send necessary data:
// Instead of full models
return Inertia::render('Users/Index', [
'users' => User::find()
->select(['id', 'name', 'email'])
->asArray()
->all(),
]);
Common Patterns ¶
Base Controller ¶
Create a base controller for shared functionality:
namespace app\controllers;
use Crenspire\Yii2Inertia\Inertia;
use yii\web\Controller;
abstract class BaseController extends Controller
{
public function init()
{
parent::init();
Inertia::share('user', function () {
return Yii::$app->user->identity;
});
}
protected function inertiaRender($component, $props = [])
{
return Inertia::render($component, $props);
}
}
Resource Controller ¶
class UsersController extends BaseController
{
public function actionIndex()
{
$users = User::find()->all();
return $this->inertiaRender('Users/Index', ['users' => $users]);
}
public function actionShow($id)
{
$user = User::findOne($id);
if (!$user) {
throw new NotFoundHttpException('User not found');
}
return $this->inertiaRender('Users/Show', ['user' => $user]);
}
public function actionStore()
{
$model = new User();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
Yii::$app->session->setFlash('success', 'User created!');
return Inertia::location(['users/show', 'id' => $model->id]);
}
return $this->inertiaRender('Users/Create', [
'errors' => $model->errors,
]);
}
}
Troubleshooting ¶
Version Mismatch Issues ¶
If you're experiencing frequent full page reloads:
- Ensure your version callback returns a stable value
- Check that
manifest.jsonexists and is readable - Verify file permissions
Inertia::version(function () {
$manifest = Yii::getAlias('@webroot/dist/manifest.json');
return file_exists($manifest) ? (string)filemtime($manifest) : '1';
});
Redirects Not Working ¶
Always use Inertia::location() instead of Yii::$app->response->redirect() for Inertia requests.
Props Not Available in Frontend ¶
- Verify props are passed as an array in PHP
- Ensure props are JSON-serializable (no closures)
- Use the
usePage()hook correctly:
import { usePage } from '@inertiajs/inertia-react';
const { props } = usePage();
const { title, user } = props;
CSRF Token ¶
Share CSRF token globally:
Inertia::share('csrfToken', function () {
return Yii::$app->request->csrfToken;
});
Use in forms:
<input type="hidden" name="_token" value={props.csrfToken} />
Additional Resources ¶
- Package Repository: GitHub - crenspire/yii2-inertia
- Inertia.js Documentation: https://inertiajs.com/
- Example Application: See the
examples/basicdirectory in the repository
Conclusion ¶
Inertia.js with Yii2 provides a powerful way to build modern SPAs while keeping the simplicity and power of server-side routing and validation. The crenspire/yii2-inertia package makes it easy to integrate Inertia.js into your Yii2 applications with a familiar API.
Give it a try and let us know what you think! For issues, questions, or contributions, please visit the GitHub repository.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.