github getkirby/kirby 5.1.0-rc.1

pre-release17 days ago

🎉 Features

Navigation for the preview view

We've added a handy page tree navigation dropdown to the preview view. Jump effortlessly to the preview of other pages #7335

Screenshot 2025-08-19 at 10 51 57

Query language

Kirby 5.1 adds a number of improvements to the Kirby Query language. Since the query language plays such an essential part for our blueprints, the new query improvements will push the possibilities for blueprints even further.

The improvements are based on a new AST-based query parser and runner that has been added in addition to the previous (legacy) parser. The legacy parser which will remain the default parser for now but we aim to replace it with the new runner in Kirby 6. Consider the new runner in beta state during Kirby 5.x releases. #6788

The new AST-based runner (thx to @rasteiner for starting off the work on this) runs not only more reliably and performant, but also allow us to add more functionalities. Those are not supported by the legacy runner.

  • Support for more operators:
    • Logic (AND/&&/OR/||)
    • Compare (==!=><,<=,>=)
    • Math (+-,* , /%)
  • Closures in queries can now accept arguments
  • Queries support subscript access notation, e.g. page[site.pageMethodName]
  • query.runner option to switch to Kirby\Query\Runners\DefaultRunner::class to activate the new AST-based runner (See docs below for more information how to create a custom runner)

The new query runner is stricter in parsing queries. In some edge cases, this will require you to access an array/object via subscript notation - in rare cases on the top level via the new this keyword, e.g. this["indentifier.with dots and spaces"].

Batch delete for structures

New batch mode for structure fields to delete multiple rows at once. You can activate it by using the new batch option in your blueprint (#7400):

structure:
  batch: true
  fields:
    text:
      type: text
structures.mov

Searchable info in multiselect fields

You can now search through the info in a multiselect field by enabling the search > info option. (Thanks to @aofn) #7089

fields: 
  multiselect: 
    type: multiselect
    options: 
      ...
    search: 
      info: true
Screenshot 2025-07-21 at 13 26 02

Stats field

We’ve turned the stats section into an additional new field. The stats section keeps existing, but with the new stats field there are more layout possibilities and options for simpler blueprint setups. The field has the exact same options as the stats section (https://getkirby.com/docs/reference/panel/sections/stats). #7392

title: Shop
fields:
  stats:
    type: stats
    size: huge
    reports:
      - label: Revenue
        value: €29,682
        info: +112.5%
        link: https://getkirby.com/shop
        theme: positive
      - label: Orders
        value: 265
        info: +82.8%
        theme: positive
      - label: Avg. Transaction
        value: €112.01
        info: +16.3%
        theme: positive
      - label: Refunds
        value: €15.20
        info: +10.5%
        theme: positive
      - label: Discount sales
        value: €1,422
        info: n/a

Imagick thumb driver

New imagick thumbs driver, using the Imagick class instead of the convert command line command #6754

// /site/config/config.php
return [
  'thumbs' => [
    'driver' => 'imagick'
  ]
];

Set the default panel.theme

You can now set the default Panel theme that is used when the user has not set any custom setting in their account view. Supported values: "light""dark" or "system" (default, matches the user's operating system) #7341

// site/config/config.php
return [
  'panel' => [
    'theme' => 'light'
  ]
];

New Collection::join() method

This new method is available in all collections #7340 (thx to @adamkiss)

$venues = $page->children()->join(' | ', fn ($child) => $child->venue());
$venues = $page->children()->join(as: fn ($child) => snippet('venue', ['page' => $child], return: true));

New session.cookieDomain option

The option allows to share the session cookie across subdomains with a shared sessions folder feedback.getkirby.com/628

Note

You can read more about the behavior of the domain attribute for cookies in the MDN docs.

Note that setting the cookieDomain option disables Kirby setting the path attribute to the cookie (relevant in subfolder setups).

return [
    'session' => [
        'durationNormal' => 7200,            // default: 2 hours
        'durationLong'   => 1209600,         // default: 2 weeks
        'timeout'        => 1800,            // default: half an hour
        'cookieDomain'   => 'getkirby.com',  // default: automatic browser behavior
        'cookieName'     => 'kirby_session',
        'gcInterval'     => 100              // default: cleanup every ~100 requests
    ]
];

Dialog search input improvements

The dialog search input has a new button to clear the input #7513

search.mov

Option to delete a license

The license dialog now includes a button to remove the license file #6962 [#6960](https://

Screenshot 2025-08-19 at 11 07 21

✨ Enhancements

Support for nested dialogs

We've added history support for panel.dialog for nested dialogs #7519 Dialogs can now correctly open more dialogs which will be stacked on top of each other and close correctly.

Improved UI for the structure field

We've improved the UI when the structure field is disabled that allows to open drawer to see all fields as well as navigate through all entries when paginated https://feedback.getkirby.com/687

Improved language detector

Our language detector will now correctly detect languages based on locales. You can now easily set up two English locales (en_GB and en_US) for example and the detector will pick the right one and redirect correctly. (thanks to @tobiasfabian) #7178

Change the panel translation via URL

You can now set the Panel interface translation on demand with the new translation query parameter. E.g. https://example.com/panel/login?translation=it #6616

Better fatal view

We've added a link to the PHP version support table from the fatal view that is displayed on unsupported PHP versions

New skeleton theme for items

We've added a new sekeleton theme for the k-item component to represent a loading state #7521

Screenshot 2025-08-12 at 11 20 14

Snippet controllers

With Kirby 5.1 you can create a small plugin that enables support for snippets and block controllers: one can add a controller for e.g. the snippet site/snippets/header.php to site/controllers/snippets/header.php. And since blocks are also just snippets, e.g. site/controllers/snippets/blocks/video.php.

As there can be many snippet calls within a single request, checking for the existence of a controller for each of these can impact your site’s performance.

<?php

use Kirby\Cms\App;
use Kirby\Template\Snippet;
use Kirby\Toolkit\Str;

class SnippetWithController extends Snippet
{
  protected static $controllers = [];

  public static function controller(
    string|null $file,
    array $data = []
  ): array {
    if (
      $file === null ||
      str_starts_with($file, static::root()) === false
    ) {
      return $data;
    }

    if (isset(static::$controllers[$file])) {
      return array_replace_recursive(
        $data,
        static::$controllers[$file]
      );
    }

    $name = ltrim(Str::before(Str::after($file, static::root()), '.php'), '/');

    // if the snippet has a name, we can load the controller
    // and merge the data with the controller's data
    if ($name !== null) {
      $data = array_replace_recursive(
        $data,
        static::$controllers[$file] = App::instance()->controller('snippets/' . $name, $data)
      );
    }

    return $data;
  }

  public static function factory(
    string|array|null $name,
    array $data = [],
    bool $slots = false
  ): static|string {
    $file = $name !== null ? static::file($name) : null;
    $data = static::controller($file, $data);
    return parent::factory($name, $data, $slots);
  }

  public function render(array $data = [], array $slots = []): string
  {
    $data = array_replace_recursive(
      static::controller($this->file, $data),
      $data
    );

    return parent::render($data, $slots);
  }
}

App::plugin('getkirby/snippet-controllers', [
  'components' => [
    'snippet' => function (
      App $kirby,
      string|array|null $name,
      array $data = [],
      bool $slots = false
    ): Snippet|string {
      return SnippetWithController::factory($name, $data, $slots);
    },
  ]
]);

And more …

  • Stats (stats field and section as well as the k-stats/k-stat components) now support passing a drawer instead of a link or dialog #7472
  • New <k-stats-field> component #7389
  • New methods Kirby\Cms\License::delete() and Kirby\Cms\License::root() #7211
    github.com//issues/6960)
  • New LanguageVariable::language() method #7379
  • New Kirby\Panel\Ui\Stat and Kirby\Panel\Ui\Statsbackend components, which are used to create the correct information for the <k-stat>/<k-stats> Vue component #7373 #7374
  • Dialog and drawer routes: support passing a controller class name or closure hat returns an object with ::load() and ::submit() methods that will be used then as load/submit event actions. Ideally used with returning a Kirby\Panel\Controller\Dialog/Kirby\Panel\Controller\Drawer object. If the named class also defines a static ::for() method, it will be used to create an instance of the class.
  • The <k-label> component now also accepts false for the input prop. This will switch the label element to an h2 and also remove the <abbr> for the invalid state. All fields that don't store any values will need this. We can also use it for the info field, which did still create its own headline.
  • New Collector classes #7433
    • New Kirby\Panel\Collector\PagesCollector class
    • New Kirby\Panel\Collector\FilesCollector class
    • New Kirby\Panel\Collector\UsersCollector class
  • New Kirby\Panel\Ui\Upload class to handle upload settings in sections and potentially also in fields #7436
  • New Model Item Classes to create all the necessary props for a <k-item> component for a model, such as text, info, image, link, etc. #7456
    • Kirby\Panel\Ui\Item\FileItem
    • Kirby\Panel\Ui\Item\ModelItem
    • Kirby\Panel\Ui\Item\PageItem
    • Kirby\Panel\Ui\Item\UserItem
  • New Uuid::toUrl() method that returns the model's absolute URL and includes query and/or fragment that was included in the UUID uri #7484
  • Better IDE support for the return type of Page::uuid(), Site::uuid(), File::uuid() and User::uuid() #7484
  • New closed listener for Panel dialogs and drawers that gets called after the dialog has been closed #7519
  • Kirby\Uuid\Uuid::is() now accepts an array of multiple types to test against #7543
  • New Kirby\Http\Query::merge() and Kirby\Http\Params::merge() methods #7546

🐛 Bug fixes

  • Toggles field:
    • Highlight selected value even when disabled. #7357
    • Stronger focus color
  • Layout field: fixed disabled styling #7358
  • Radio and checkboxes field: fixed styling when disabled #7359
  • currentColor isn't enforced as fill for all SVGs anymore (only for .k-icon) #7297
  • Inputs: Hide placeholder text when disabled #7360
  • Kirby\Panel\Ui\Component::render() now only filters out props that are null, not empty arrays #7384
  • Fixed destructive CSS rules in the Lab for h2 that easily bled into component rules.
  • Add a space before or after HTML tags before applying strip_tags, but only if they are adjacent to word characters. #7306
  • UUID for new page is the same as the latest page UUID #7405
  • Fixed some Panel route regular expressions that sometimes would match more than wanted #7467
  • Fixed a case where a subclass of Kirby\Http\Response would falsely converted to a Kirby\Http\Response object #7478
  • Fixed UUID permalinks for languages with custom URL/without a URL prefix #7450
  • Range input: Tooltip keeps a stable width based on the max value #7417
  • Support query and fragments in link KirbyTag when using a UUID #7477
  • No more orphaned media files when changing a page's status to draft #6573
  • Closing all drawers now doesn't leave the darkened overlay visible #6030
  • Subsequent Panel view request cancel out ongoing (slow) previous view request to ensure navigation always lands at the latest requested view #6536
  • Show loading spinner during dialog and drawer refreshes #7514
  • Fixed dialog button layout when there's only one button #7519
  • Support more MIME type fors .wav (thx @pReya) #7531
  • Writer field: Hide nodes dropdown when only one node available #7305

☠️ Deprecated

  • The ::url() method of PageUuid and FileUUid has been renamed to ::toPermalink() and will be removed in a future release. #7484
  • The im thumbs driver option has been deprecated. Use imagick instead. #6754
  • Kirby\Http\Router::find(): Passing $ignore = null has been deprecated (pass an empty array or omit the argument instead) #7547

♻️ Refactored

  • Move since tag to own section in Lab docs #7296
  • Support App::controller() to return controllers nested in subfolders inside site/controllers #7368
  • Move empty reports handling from <k-stats-section> to <k-stats> #7376
  • Export props from <k-stats>
  • Simplified config for area dialogs, drawers and drop-down #7377
  • The stats field uses the new Kirby\Panel\Ui\Stat and Kirby\Panel\Ui\Stats classes to clean up the props generation for the frontend.
  • New Kirby\Form\Mixin Traits for after, autofocus, before, help, icon, label, placeholder and width props #7396
  • Improve the inline docs for all field mixins. #7396
  • Use the new Collector classes in the files and pages section, as well as the users view. #7435
  • Always use the list settings for table layouts in Kirby\Panel\Model::image() #7449
  • Use new Model Item classes in the files and pages sections and the users view to clean up the code. #7456
  • Use new Model Item classes in ::pickerData() methods and in the Kirby\Panel\Controller\Search controller to clean up all places where such item props are created. #7457
  • Support passing a Kirby\Http\Response object to Kirby\Cms\Responder::send(). #7478
  • Refactored users view as controller #7476

🧹 Housekeeping

  • Switch to PHP attributes for PHPUnit annotations
  • Remove Vite's deprecated splitVendorChunkPlugin. #7354
  • Replace deprecated $mock->addMethods() in unit tests #7356
  • Using PHP attributes for PHPUnit annotations in Kirby\Database package #7318
  • Remove unused methods from <k-stats>
  • Using nullish coalescing assignment in JavaScript #7515
  • Clean up code style of the Kirby\Http classes #7547

📚 Additional Docs

Custom Query Runners

query.runner config option

Kirby offers a new runner for its query syntax based on parsing the query as abstract syntax tree. This runner is more reliable and efficient than the rudimentary legacy runner (which in Kirby 5 remains the default). The new runner is currently in its beta phase. You can activate it by setting the query.runner config option:

<?php

use Kirby\Query\Runners\DefaultRunner;

return [
  'query.runner' => DefaultRunner::class
];

Query syntax

When using the new query runner, you can also use logical, comparison and math operators in your queries as well as receive arguments in closures:

// new operators
$query = new Query('age > 25 && name != "John"');
$query = new Query('price + vat');

// arguments in query closures
$query = new Query('(foo) => foo.homer');
$data  = [];

$bar = $query->resolve($data);
$bar = $bar(['homer' => 'simpson']);
$this->assertSame('simpson', $bar);

Queries will still be tried to resolve first directly from the data context, e.g. when your query is null and your data ['null' => 'foo'] the result will directly be 'foo'. Same for user.username and ['user.username' => 'foo'].

Don't miss a new kirby release

NewReleases is sending notifications on new releases.