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

Kirby 3.7


New stats section

Turn your dashboard into a smart report

The stats section is used here for a shop site to show revenue, number of orders, average transaction price, refunds and discounted sales.

Easy as 1-2-3

Show beautiful stats for your site or shop. Revenues, transactions, Instagram likes, page views… it’s totally up to you. Add as many reports to a stats section as you need. Customize reports using our query syntax, and easily integrate into page models or site methods.
  type: stats
    - label: Revenue
      value: "€29,682"
      info: +112.5%
      link: https://awesome.shop
      theme: positive
    - label: Transactions
      value: "{{ page.transactions }}"
      info: "{{ page.transactionsIncrease }}"

New table layout

Who said spreadsheets cannot be cool?

The new table layout is a lot more compact and can show custom data more conveniently for pages and files

At a glance

With the brand new table layout for pages and files sections, you get a great overview of our content. Customize the columns you want to show to present exactly the data you need.
  type: pages
  layout: table
      label: Subtitle
      label: Date
      type: date
      display: DD.MM.YYYY
      label: Tags
      type: tags

New system view

Everything system related at a glance

The system view has a new design and the new security section with a list of warnings


Vital information about your environment: License key, version number, PHP version, server software.


Find potential issues and unintentionally exposed parts of your installation (Git repo, content folder, site folder, kirby folder).


View all installed plugins, their authors, license and current version number in our redesigned plugins table.

New toggles field

Stylish options you will love

The toggles field with four text buttons for text alignment
  type: toggles
  label: Labels
    # Short version with value: text
    left: Left
    center: Center
    right: Right
    justify: Justify
The toggles can combine text and an icon for a more visual representation
  type: toggles
  label: Icons & Labels
    # Detailed version with more options
    - value: left
      text: Left
      icon: text-left
    - value: center
      text: Center
      icon: text-center
    - value: right
      text: Right
      icon: text-right
    - value: justify
      text: Justify
      icon: text-justify
The toggles can be just icons and have the label text as title attribute on the label in this case
  type: toggles
  label: Icons
  labels: false
    # Text will be used as title
    # attribute for the <label>
    - value: left
      text: Left
      icon: text-left
    - value: center
      text: Center
      icon: text-center
    - value: right
      text: Right
      icon: text-right
    - value: justify
      text: Justify
      icon: text-justify
The toggles can also be shown in a more compact form
  type: toggles
  label: Icons
  labels: false
  grow: false
    - value: left
      text: Left
      icon: text-left
    - value: center
      text: Center
      icon: text-center
    - value: right
      text: Right
      icon: text-right
    - value: justify
      text: Justify
      icon: text-justify

Refined UI

A fresh new look for your Panel

The card design shows the new rounded corners that have been introduced throughout the Panel
Fields also feature a more rounded look

Fresh & familiar

Slightly rounder, more friendly, and more cosy: you will feel instantly at home.

New icons

  • archive
  • badge
  • brush
  • discord
  • discount
  • folder-structure
  • github
  • money
  • palette
  • pen
  • store
  • ticket

New file icons

The placeholder icons for all kinds of file types – documents, videos, spreadsheets, etc.



  • Brand new statistics section
  • New search option for pages and files sections https://kirby.nolt.io/14 #4316
  • New field previews in tables for the tags, radio, select, multiselect, checkboxes, structure, blocks, layout, files, pages and users fields. #4294 #4195
  • New system health checks to detect unintentionally exposed parts of your installation (Git repo, content folder, site folder, kirby folder)
  • $request->auth() now also handles Kirby's Session auth type automatically #4264
  • The auth types for $request->auth() can now be extended by plugins #4264
  • New $kirby->response()->usesAuth() and ->usesCookie() method to tell Kirby's automatic caching system about used Authorization headers and cookies. These methods are called automatically when the Kirby Request and Cookie classes are used.
  • New $kirby->request()->hasAuth() method to check the existence of the Authorization header #4277
  • New $kirby->isNativeComponent($component) method #4348
  • New k-table component to create consistent tables easily
  • Styled preview for tags in tables
  • New JS helper $helpers.object.isEmpty(value)

New Str methods

Str::afterStart() and Str::beforeEnd()

… to remove a substring only from the start or end of a string #4339

Str::camel() and Str::studly()

… for case-altering of strings #4338

echo Str::camel('foo_bar'); //=> fooBar
echo Str::studly('foo_bar'); //=> FooBar


… for appending a number -1 to a string or incrementing the ending number to allow -2, -3, etc. #4340

echo Str::increment('Page-1'); //=> Page-2
echo Str::increment('Page', '_'); //=> Page_1


… for wrapping the string with the given strings.

echo Str::wrap($string, '# ', ' {.title}'); //=> # Post title {.title}
echo Str::wrap($string, '"'); //=> "Post title"

New validators


… to validate correctly formatted JSON strings.

if (V::json('{"foo": "Foo"}')) {
  // do something...

V::empty() and V::notEmpty()

To check for empty values in PHP is not as easy as it sounds like. empty() is often giving wrong results. Especially if you pass 0 or '0'. The new validators help with this task. They also serve as underlying logic for a better V::required()validator. It can now be used in two ways:

With a reference array …

V::required('myField', ['myField' => 'Some value']);

or just a value …

V::required('My value');

This makes the method more versatile and it can be used in V::value():

V::value('My value', [
  'required' => true,
  'minlength' => 5,


Custom helpers

Helpers can now be deactivated by setting a global constant named KIRBY_HELPER_*, e.g. KIRBY_HELPER_DUMP to false #4018 Check out the documents.

In some cases, our global helper functions will collide with other tools' global functions if they use the same name, e.g. dump(). If you are affected by this problem, you can now set the matching constant, e.g. KIRBY_HELPER_DUMP, to false and Kirby will not register its global helper.

To ensure that Kirby still functions properly, we moved all functionality from the helper functions to class methods (and the global helper functions are only aliases now). We recommend that plugin developers also start using these class methods to ensure that your code always uses Kirby's core helpers:

  • New Cms\App::csrf() method with the functionality of the csrf() helper
  • New Cms\App::image() method with the functionality of the image() helper
  • New Cms\Helpers::deprecated() method with the functionality of the deprecated() helper
  • New Cms\Helpers::size() method with the functionality of the size() helper
  • New Cms\Helpers::dump() method with the functionality of the dump() helper
  • New Cms\Html::css() method with the functionality of the css() helper
  • New Cms\Html::js() method with the functionality of the js() helper
  • New Cms\Html::svg() method with the functionality of the svg() helper
  • New Filesystem\F::loadClasses() method with the functionality of the load() helper
  • New Http\Router::execute() method with the functionality of the router() helper
  • New Http\Response::go() method with the functionality of the go() helper https://kirby.nolt.io/369
  • New Toolkit\Date::roundedTimestamp() method with the functionality of the timestamp() helper
  • New Toolkit\Str::esc() method with the functionality of the esc() helper
  • New Toolkit\Str::uuid() method with the functionality of the uuid() helper
  • New Toolkit\V::invalid() method with the functionality of the invalid() helper
  • Extended Cms\App::kirbytag() method with the functionality of the kirbytag() helper. Allows to pass an array as first parameter.
  • Extended Cms\App::snippet() method with the functionality of the snippet() helper.
  • Extended Toolkit\Html::attr() method with the functionality of the attr() helper. Allows to pass $before and $after string.


  • Added Kirby’s PHP and extension dependencies to composer.json #4216
  • Toolkit\Xml::value() now uses better logic to determine whether a CDATA block is needed. #4335
  • Methods in Kirby's core now use the global environment object instead of $_SERVER wherever possible
  • JSON errors in debug mode now contain the file path relative to the document root for consistency with the API that already handles it like this.
  • Kirby's response handling now automatically controls page and HTTP caching based on cookies, session and the Authorization request header used by the response. If any of these are used and also contained in the request, the response is considered private. If they are used but not contained in the request, the page is only cached for visitors without these cookies/session/auth. Manual sessions are always considered private. #3976
  • $pages->find() can now find pages by their translated URI in secondary languages of multi-lang setups, even in collections without a parent (like $page->grandChildren())


  • We've upgraded to Vite 2.9.8 #4289
  • k-items now supports table layouts #4328
  • k-collection supports empty state via empty props (passing text and icon keys) #4247
  • New k-image-field-preview and k-flag-field-preview components #4328

Bug fixes


  • Fixed return type hints to include null as possible return value for Cms\Pagination::firstPageUrl()
    and Cms\Pagination::lastPageUrl() #4314
  • Fixed a bug where field content was copied to secondary languages even though translate: false was set #2577
  • Fixed a bug where fields with uppercase field names in virtual pages could not be searched #4142
  • File permissions of all non-executable files in the Kirby repo have been set to 644. #4336
  • Editing a page title before the translation file had been created caused untranslatable fields to be copied to the translation file #2789
  • kirby->url() no longer adds the wrong port or protocol on nginx / reverse proxy #4311
  • Url overwrite config now also overwrites protocol correctly #4234
  • file::url component no longer breaks the preview URL in the Panel #4143
  • Fixed a regression that prevented page and HTTP caching when the session was accessed, even when the session was not active #3976
  • The behavior of the $pages->find(), $pages->findById() and $pages->findByUri() methods in non-direct-child collections (e.g. $page->grandChildren()) is now consistent between single-lang and multi-lang installations #4105


  • k-empty now actually supports its text prop
  • Improved structure field columns on mobile #3638
  • Structure field sticky header inside drawers #4242
  • Fixed wrong draggable ghost element for structure field rows #4242
  • Preview image from other page was not shown when the page is a child of site and status published was queried #4297
  • Uploading multiple files now adds correct sorting numbers #4317
  • Better responsiveness for Panel section headers #4316
  • Fixed Copy & Paste in list blocks on the first try #3974
  • The Panel and API now send the Cache-Control: no-store, private response header instead of just Cache-Control: no-store for better compatibility with caches like CloudFlare #4299


New Http\Environment class

The new object-oriented Http\Environment class replaces the former static Http\Server class and takes over all its methods.

It takes care of detecting all necessary parts (host, port, https, etc.) and considers an optional allow list of URLs to validate the current environment:

$env = new Environment([
  'allowed' => [

I.e. if it finds to be running on the test.getkirby.com host from above, https is on and the port is 9999 and the subfolder also matches, you can get all those parts now correctly via:


We use this new class as foundational element in our $kirby instance to construct absolute and relative URLs, roots and paths. You can access it and its methods with $kirby->environment(). Kirby internally uses the Environment class to set the site's base URL based on the url option.


  • Improved internal usage of router callbacks #4260
  • The Kirby core no longer uses Kirby's helper functions internally but calls the underlying methods directly. #4018
  • Content field names are now always handled case-insensitively.
  • Kirby's composer.json no longer pins the psr/log library to a specific version #4392


  • Structure field uses k-table
  • New internal components k-structure-form
  • Table block preview uses k-table
  • New internal k-models-section #4247
  • Refactored k-pages-section and k-files-section to use (extend) k-models-section #4247
  • Upgraded JS dependencies #4329


  • The dump component was deprecated and superseded by the new feature to replace helpers (KIRBY_HELPER_DUMP constant). The component will be removed in Kirby 3.8.0. #4018
  • Http\Server class will be removed in 3.8.0
  • The constants in Http\Server:: are now deprecated and will be removed in 3.8.0. They are no longer needed.
  • The $pages->findById() and $pages->findByUri() methods have been deprecated and will be removed in 3.8.0. If you want to directly get a page by ID, use $pages->get(). If you want the previous fuzzy behaviour that queries both by ID and URI, use $pages->find().
  • The $pages->findByIdRecursive() method has been deprecated and will be removed in v3.8.0. Please use $pages->find() instead.
  • The $files->findById() method has been deprecated and will be removed in v3.8.0. Please use $files->find() instead.
  • The $pages->findByKey(), $files->findByKey() and $users->findByKey() methods have been marked as internal. Please use the find() methods instead.
  • The $inline parameter in $kirby->kirbytext() is deprecated. Use $options['markdown']['inline'] instead.
  • The $inline parameter in $kirby->markdown() is deprecated. Use $options['inline'] instead.
  • Blueprint presets page, pages and files are deprecated.

Deprecation warnings

The following deprecated methods/parts throw warnings in 3.7.0 and will be removed starting in 3.8.0 #4335


Deprecated Use instead Removed in
$file->dragText() $file->panel()->dragText() 3.8
$file->panelIcon() $file->panel()->icon() 3.8
$file->panelImage() $file->panel()->image() 3.8
$file->panelOptions() $file->panel()->options() 3.8
$file->panelPath() $file->panel()->path() 3.8
$file->panelUrl() $file->panel()->url() 3.8
$file->pickerData() $file->panel()->pickerData() 3.8
$files->findById() $files->find() 3.8
$kirby->server() $kirby->environment() 3.9
$page->dragText() $page->panel()->dragText() 3.8
$page->panelIcon() $page->panel()->icon() 3.8
$page->panelId() $page->panel()->id() 3.8
$page->panelImage() $page->panel()->image() 3.8
$page->panelOptions() $page->panel()->options() 3.8
$page->panelPath() $page->panel()->path() 3.8
$page->panelUrl() $page->panel()->url() 3.8
$page->pickerData() $page->panel()->pickerData() 3.8
$pages->findById() $pages->find() 3.8
$pages->findByIdRecursive() $pages->find() 3.8
$pages->findByUri() $pages->find() 3.8
$site->panelIcon() $site->panel()->icon() 3.8
$site->panelImage() $site->panel()->image() 3.8
$site->panelOptions() $site->panel()->options() 3.8
$site->panelPath() $site->panel()->path() 3.8
$site->panelUrl() $site->panel()->url() 3.8
$site->pickerData() $site->panel()->pickerData() 3.8
$user->panelIcon() $user->panel()->icon() 3.8
$user->panelImage() $user->panel()->image() 3.8
$user->panelOptions() $user->panel()->options() 3.8
$user->panelPath() $user->panel()->path() 3.8
$user->panelUrl() $user->panel()->url() 3.8
$user->pickerData() $user->panel()->pickerData() 3.8
Panel\Document::customCss() Panel\Document::customAsset('panel.css') 3.8
Panel\Document::customJs() Panel\Document::customAsset('panel.js') 3.8


Deprecated Use instead Removed in
API field page.next - 3.8
API field page.prev - 3.8

Breaking changes

We try to avoid breaking changes as much as we can. But we also put a lot of effort in keeping our technical debt in Kirby as low as possible. Sometimes such breaking changes are necessary to move forward with a clean code base.

You might wonder why there are breaking changes in a minor release according to Semantic Versioning.

We stick to the following versioning scheme:


This release is Kirby (major release 7 of Kirby 3)

Traditionally, we combine patch and minor releases though and only need the fourth versioning level for regression fixes.

Composer installation

If you install Kirby using Composer, you may run into errors if your command line installation of PHP doesn’t have the extensions installed that Kirby requires. If you are sure that your web server fulfills the requirements, you can use Composer’s --ignore-platform-reqs or --ignore-platform-req=ext-* flags. #4216


  • Http\Router::$beforeEach and Http\Router::$afterEach aren't static anymore. Pass them in an array as second argument to the constructor instead. #4260
  • Custom auth type classes in the Kirby\Http\Request\Auth\ namespace now need to be registered in the Kirby\Http\Request::$authTypes array. We also recommend to move the classes to your own namespace to avoid interference with core classes. #4264
  • Field names of virtual pages' content are now converted to lowercase. If your virtual page has two fields that only differ in capitalization, only the last defined one will be available.
  • The Server::hosts() method was removed. Please set the allowed URLs via the url option.
  • page helper now always only returns a Kirby\Cms\Page object or null, never a pages collection. Only allows passing a single id as parameter. #4335
  • pages helper now always only returns a Kirby\Cms\Pages collection or null, never single page object. #4335
  • Creating a Kirby\Cms\File object requires the parent property now. #4335
  • Toolkit\Str::toBytes() strictly only accepts a string as parameter now. #4335
  • The $pages->children(), $pages->drafts() and $pages->index() methods no longer set the $pages->parent() object as the collection items can have multiple different parents. This can change the behaviour when finding collection items in secondary languages and when merging collections. #4105
  • The dump() and e() helper function checks have been removed. If your plugins or dependencies have functions with these two names, you will get an error. You can override the conflicting functions by defining the KIRBY_HELPER_* contants. Check out how to do it.
  • The second argument of the kirbytextinline() and kti() helpers has been renamed from $data to $options

Removed methods

The following deprecated methods/parts have been removed #4335

Removed Use instead
Cms\App::setLocale() Toolkit\Locale::set()
Cms\Block::_key() Cms\Block::type()
Cms\Block::_uid() Cms\Block::id()
Toolkit\I18n::fallback() Toolkit\I18n::fallbacks()
Toolkit\Str::template(): $fallback, $start and $end parameters pass an array to the $options parameter


  • k-block-type-table and k-structure-field have been largely refactored. If you (or a plugin) extends these, stuff might break.
  • k-pages-section and k-files-section have been modified extensively #4247
  • Toolkit\Html::attr([], null) now consistently returns null instead of an empty string. #4018
  • Removed builder and editor field migration for blocks field.


The following deprecated methods/parts have been removed #4335

Removed Use instead
API fields page.panelIcon / file.panelIcon page.panelImage / file.panelImage
GET (:all)/lock API endpoint -
GET (:all)/unlock API endpoint -
GET pages/(:any)/children/blueprints API endpoint GET pages/(:any)/blueprints
GET site/children/blueprints API endpoint GET site/blueprints
JS API method files.rename() files.changeName()
JS API method pages.slug() pages.changeSlug()
JS API method pages.status() pages.changeStatus()
JS API method pages.template() pages.changeTemplate()
JS API method pages.title() pages.changeTitle()
JS API method site.title() site.changeTitle()
JS API method system.info() system.get()


Get started