Kirby Inertia
Inertia.js adapter for Kirby 3.
Inertia allows you to build your front end using Javascript (React, Vue and Svelte), while letting the server (in this case, Kirby) take care of routing and shaping data. Think of it as building a normal Kirby site, but the front end is rendered with Javascript.
How it works
The basic idea is when loading your site in the browser, Kirby serves up an HTML page with the page data inlined as JSON so your Javascript application can render the page. As you navigate to different pages, requests are sent to Kirby with an X-Inertia
header, informing Kirby to return only the JSON for the page, rather than a full HTML response. Your Javascript application picks up on this data and renders each new page. You can read a bit more about how it works on the Inertia site.
Setup
After installation, the bare minimum you need to do is define a default.php
template:
<!DOCTYPE html>
<html>
<head>
<title><?= $site->title() ?></title>
<?= css('assets/css/site.css'); /* Your site’s css */ ?>
</head>
<body>
<?php snippet('inertia') ?>
<?= js('assets/js/site.js'); /* Your site’s js */ ?>
</body>
</html>
The included inertia
snippet simply renders an application shell with the current page data encoded as JSON:
<div id="app" data-page="{}"></div>
At this point you can go ahead and build your Inertia front end. If you visit your site in the browser (and view source), you’ll see the application shell with page data inlined. If you were to request that same url with an X-Inertia
header (test using wget/curl or Postman), a JSON response will be returned instead.
Creating Responses
Inertia responses in Kirby are created with Kirby Controllers. The only difference with a typical Kirby controller is you return an Inertia::render
function, instead of an array. This plugin assigns a default.php
controller for you:
return function ($page, $site, $kirby) {
return Inertia::render(
$page->intendedTemplate(),
$page->toArray()
);
};
The first argument specifies the name of the view your Javascript application should render, the second argument is the page data. In this case we’re using the $page->intendedTemplate()
as the view name, and the $page->toArray()
method to pass the page data.
Here's an example Controller with more specific control:
return function ($page, $site, $kirby) {
return Inertia::render('TemplateName', [
'title' => $page->title()->value(),
'date' => $page->date()->value(),
'thumbnail' => $page->thumbnail()->toFile()->url(),
'content' => $page->text()->kirbytext()
]);
};
Inertia Features
The following sections outline how to use some of Inertia's features in Kirby since they differ slightly with the Laravel adapter.
Lazy evaluation
Lazy evaluation and partial data works as expected, just wrap your data in closures:
return function ($page, $site, $kirby) {
return Inertia::render('TemplateName', [
'title' => $page->title()->value(),
'lazyProp' => function () use ($page) {
return $page->children()->toArray();
}
]);
};
Shared Data
Unlike the Laravel adapter, Shared Data in Kirby is defined in your config.php
. You can define shared data as an array (values can be closures) or as a closure which returns an array:
'monoeq.inertia.shared' => [
"prop" => "value",
"beep" => function () {
return "boop";
}
]
'monoeq.inertia.shared' => function () {
return [
"prop" => "value"
];
}
Form Handling
Handle forms in your Kirby controller like you usually would. Just make sure you redirect to a view as explained in the Inertia docs:
return function ($page, $site, $kirby) {
// Form Handling
if (kirby()->request()->method() === 'POST') {
$kirby->impersonate('kirby');
$page->changeTitle(get('title', ''));
go($page); // <- Redirect back to GET request for Inertia
}
return Inertia::render(
$page->intendedTemplate(),
$page->toArray()
);
};
Error Handling
You'll want to handle errors in the controller as well. An InertiaSession
helper is included for passing data to the Kirby session, that you can then pick up on in your shared data. See an example here.
Root template data
You can access data in your Kirby templates with the via the $inertia
variable. Example: $inertia['prop']
With View Data
Kirby Inertia does not have a withViewData
method, instead, you can optionally pass a 3rd param into Inertia::render
. This just passes data into the kirby template like the normal controller behavior.
return function ($page, $site, $kirby) {
return Inertia::render(
$page->intendedTemplate(),
$page->toArray(),
[ 'meta' => 'hello' ]
);
};
// In your template:
<?= $meta ?>
Session Data
An InertiaSession
helper is included for passing session data to your Inertia views. This is really helpful for form error handling or flash messages, and is similar to Laravel's Inertia::share
. You can handle your Inertia session data when you set up your shared data in config.php
:
return [
'monoeq.inertia.shared' => [
'messages' => function () {
// pull() fetches any session data stored under messages, and then wipes it
return InertiaSession::pull('messages');
},
'errors' => function () {
// pull() fetches any session data stored under errors, and then wipes it
return InertiaSession::pull('errors');
}
]
];
So you can imagine in a form controller, using this helper like so:
return function ($page, $site, $kirby) {
// Form Handling
if (kirby()->request()->method() === 'POST') {
try {
$kirby->impersonate('kirby');
$page->changeTitle(get('title', ''));
InertiaSession::append('messages', 'Thank You!');
} catch (Exception $e) {
InertiaSession::append('errors', $e->getMessage());
// or if you want to have named errors
// InertiaSession::merge('errors', [ 'title' => $e->getMessage() ]);
}
go($page); // <- Redirect back to GET request for Inertia
}
return Inertia::render(
$page->intendedTemplate(),
$page->toArray()
);
};
From there, in your Javascript views you can pick up on this data
<div v-if="$page.messages">{{ $page.messages }}</div>
<div v-if="$page.errors">{{ $page.errors }}</div>
Config
Setup versioning and shared data in your config.php
.
return [
'monoeq.inertia.version' => '1.0',
'monoeq.inertia.shared' => [
'site' => function () {
return [
'title' => site()->title()->value()
];
}
]
];
Classes API
Inertia
Inertia::render($name, $data, $viewData)
Return from your Kirby Controllers to render an Inertia response.
InertiaSession
Wrapper class around kirby()->session()
for passing data to your Inertia views. Data is namespaced under the hood with inertia
to avoid conflict with other Kirby session data.
InertiaSession::set($key, $value)
Wrapper around $session->set()
InertiaSession::append($key, $value)
Appends (or sets if not yet defined) value to the desired key.
InertiaSession::append('messages', 'beep');
InertiaSession::append('messages', 'boop');
InertiaSession::get('messages'); // => ['beep', 'boop']
InertiaSession::merge($key, $value)
Merges (or sets if not yet defined) value to the desired key.
InertiaSession::merge('messages', [ 'beep' => 'boop' ]);
InertiaSession::merge('messages', [ 'bleep' => 'bloop' ]);
InertiaSession::get('messages'); // => [ 'beep' => 'boop', 'bleep' => 'bloop' ]
InertiaSession::get($key)
Wrapper around $session->get()
InertiaSession::pull($key)
Wrapper around $session->pull()
InertiaSession::remove($key)
Wrapper around $session->remove()
Installation
Download
Download and copy this repository to /site/plugins/kirby-inertia
.
Git submodule
git submodule add https://github.com/monoeq/kirby-inertia.git site/plugins/kirby-inertia
Composer
composer require monoeq/kirby-inertia
Other Notes
Auto Templates
Typically in Kirby you need to create an actual template file for a controller to be called. But when using Inertia you usually only need the default.php
template. As a helper, this plugin automatically assigns any controller file which does not have a matching template file to default.php
, allowing you to just create controllers without worrying about creating templates.
Example Front End
Refer to the Inertia.js docs for how to build an Inertia front end, but here's a bare minimum example using Vue:
app.js
See code
import { InertiaApp } from '@inertiajs/inertia-vue'
import Vue from 'vue'
import 'nprogress/nprogress.css'
Vue.use(InertiaApp)
const app = document.getElementById('app')
// Include templates here
const templates = {
'default': require('./templates/default').default
}
new Vue({
render: h => h(InertiaApp, {
props: {
initialPage: JSON.parse(app.dataset.page),
// Falls back to default template, Kirby-style
resolveComponent: name => templates[name] || templates['default']
},
}),
}).$mount(app)
templates/default.vue
See code
<template>
<div>{{ content.title }}</div>
</template>
<script>
export default {
props: {
content: Object
}
}
</script>
Todo
-
Caching
- Enabling the Kirby cache will currently break Inertia functionality.