🚀 A new era: Kirby 4 Get to know
Skip to content


We’ve spent the last months working on a brand-new backend architecture for the Panel as part of Kirby 3.6. Moving away from a single page application to a more traditional approach with a simplified server-rendered backend. The new architecture is inspired by Inertia.js and simplifies our Panel and Panel plugins in ways that we’ve never dreamed of.


The single page application architecture has served us well in Kirby 3 until now. But we also realized that adding features always means adding tons of JavaScript. Every new route, dialog, every new field type and section increases the amount of JS the Panel will send back to our users – and there's hardly any way out of it. But we want less JS, not more.

This is where the new Fiber concept really shines. It takes all the routing and state management back to the server. Whatever can be prepared in PHP will be prepared in PHP, and our Vue application is mainly responsible for displaying the Panel afterwards and adding the interactive bits and pieces. If you've ever heard of Turbolink, Pjax or similar approaches, you already know mostly what Fiber does.

Here's how Fiber works

When you visit the Panel for the first time, a regular HTTP request is sent to the server. The server will render the Panel HTML document, which looks like this (simplified):

    <link rel="stylesheet" href="/app.css">
  <div id="app"></div>
    const fiber = '<?= json_encode([
      'user' => [...],
      'view' => [...],
      'translation' => [...],
      // etc.
    ]) ?>';
  <script src="/app.js">

The JS fiber object is populated with data from PHP. The server injects all the data that the Panel needs for that particular view that way.
Now Vue.js kicks in and only renders that view with the data in the fiber object.


When you now click on a link in the Panel, the event is intercepted and a JS fetch request is sent to the server. This fetch request will no longer return the entire Panel document, but only the updated Fiber object for the new view.

  "user": {...},
  "view": {...},
  "translation": {...}
  // etc.

Fiber now does two things:

  1. It updates the URL in the browser.
  2. It refreshes the Vue app with the new state from the new Fiber object.

Whenever you reload the same URL afterwards, you go back to the beginning and the server will return the full document again with the same state.

What makes this approach so great?

  • Routing is done in PHP with our trusted Kirby router.
  • We can control what exact data needs to be sent for each view.
  • State can be handled on the server with good old sessions.
  • The API is not affected by any of this and can be a lot less specific.
  • The bundle size does not blow up with new routes or new requirements for state management.
  • Less logic is needed in JS, which results in a lot less complicated Vue components. You throw in state via props and that's it.
  • Every view is super easy to test on the backend with PHPUnit.

Not just views

We've taken this concept and extended it to more parts of the Panel: Panel areas can include views, dialogs, dropdowns or even search types. All are handled with a similar approach and we think we can extend this even further in future releases.

A lot less JS

With this approach, we already managed to cut a huge 100 KB+ chunk out of our Vue.js application. It's massive and it's so much fun.

Why not just Inertia.js?

Fiber is mainly inspired by Inertia.js. But the more we dug into the concept, the more ways we found how we can adjust and improve the principles of Inertia for our own needs. First, we implemented our own client implementation of Inertia. Then we started changing lots of parts on the backend. In the end, the basic request concept is still the same, but the implementation is very different. It felt confusing to use the same name. You cannot look up things in the Inertia.js docs when you work with the Panel. Dialogs and other ideas for future Fiber Panel features don't exist in Inertia. Fiber seemed like an appropriate alternative term.