Extending Kirby CMS: Custom Hooks, Routes and Vue 3 Panel
A client launched an online store on Kirby CMS and hit limitations: built-in fields did not support product-to-CRM linking, and images needed conversion to WebP. Standard tools lacked flexibility — we had to write a plugin. Such situations arise regularly when business logic exceeds out-of-the-box functionality.
Our plugins can integrate with external services, add custom fields to the control panel in Vue 3, override routes for API, or execute background tasks via hooks. For example, when publishing an article, automatically generate WebP images and send a Telegram notification. And all this without altering Kirby's source code, simplifying updates and reducing the risk of breakage. We develop custom plugins — PHP packages that extend the CMS core without modifying its code. Contact us for a preliminary assessment of your project.
With over 5 years of experience and 50+ successful Kirby projects, we deliver robust plugins. We guarantee compatibility with Kirby 4+ and provide a 30-day support period. Custom plugin development starts from $500 for simple hooks and ranges up to $5000 for complex panel integrations.
How to Develop a Custom Plugin for Kirby?
Stack: PHP 8.3, Kirby 4, Vue 3 (for Panel), Composer, Docker. We use Kirby::plugin() to register extensions. Typical folder structure includes index.php, lib/, templates/, assets/.
<?php // site/plugins/my-plugin/index.php use Kirby\Cms\App; use Kirby\Cms\Page; App::plugin('vendor/my-plugin', [ 'options' => [ 'cache' => true, 'apiKey' => null, ], 'fields' => require __DIR__ . '/lib/fields.php', 'methods' => require __DIR__ . '/lib/methods.php', 'routes' => require __DIR__ . '/lib/routes.php', 'hooks' => require __DIR__ . '/lib/hooks.php', 'blueprints' => require __DIR__ . '/lib/blueprints.php', 'translations' => [ 'ru' => require __DIR__ . '/lib/translations/ru.php', 'en' => require __DIR__ . '/lib/translations/en.php', ], ]); How to Avoid N+1 Queries and Overloading the Server?
Without proper hooks, each page request spawns dozens of redundant SQL queries. Our approach implements caching at the plugin level: we clear the cache only when a specific page changes, not the entire site. This reduces server load by 3–5 times — up to 80% in some cases. In one project, integration with Meilisearch cut search time from 2 seconds to 50 ms. Get a free consultation for your project.
<?php // lib/hooks.php return [ 'page.create:after' => function (Page $page) { if ($page->intendedTemplate()->name() !== 'article') return; $url = urlencode(kirby()->site()->url() . '/sitemap.xml'); @file_get_contents("https://www.google.com/ping?sitemap={$url}"); }, 'page.update:after' => function (Page $newPage, Page $oldPage) { if (kirby()->cache('pages')->exists($newPage->id())) { kirby()->cache('pages')->remove($newPage->id()); } }, 'file.create:after' => function (\Kirby\Cms\File $file) { if (!$file->isImage()) return; $file->thumb(['width' => 800, 'format' => 'webp']); $file->thumb(['width' => 400, 'format' => 'webp']); }, ]; How to Integrate External APIs via Custom Routes?
Often we need to fetch data from CRM or billing systems. Through custom routes, we create secure endpoints returning JSON or XML. In one project, we built product search via Meilisearch — results delivered in 50 ms instead of 2 seconds. That's 40x faster than standard search.
What custom fields can be added to the control panel?
The standard field set isn't always suitable. We develop Vue components: colorpicker, tag-input, map-picker. These speed up editors' work and eliminate input errors.
<?php // lib/fields.php return [ 'colorpicker' => [ 'props' => [ 'value' => function ($value = '#000000') { return $value; }, 'presets' => function (array $presets = []) { return $presets; }, ], 'save' => function ($value): string { if (!preg_match('/^#[0-9A-Fa-f]{6}$/', $value)) { throw new \Exception('Invalid color format'); } return $value; }, ], ]; // assets/js/fields.js panel.plugin('vendor/my-plugin', { fields: { colorpicker: { template: ` <k-field v-bind="$props"> <input type="color" :value="value" @input="$emit('input', $event.target.value)"> <div class="presets"> <button v-for="color in presets" :key="color" :style="{ background: color }" @click="$emit('input', color)" /> </div> </k-field> `, props: { value: String, presets: Array }, }, }, }); Register the asset in index.php: 'assets' => ['js/fields.js' => __DIR__ . '/assets/js/fields.js'].
In Kirby, hooks are available for all major events: creating, updating, deleting pages and files, rendering templates, sending emails, and more. Hooks allow you to inject business logic without modifying site code. According to the official Kirby documentation, plugins are the preferred method of extending functionality.
Why Custom Plugins Are Better Than Core Modifications?
Direct changes to Kirby's core lead to upgrade issues: each new release can overwrite your edits. Plugins live in their own isolated folder and use only the public API. With proper semantic versioning, your plugin stays compatible with new versions without extra work. This saves time and money on maintenance.
Caching Approach Comparison
Table: Caching approaches
| Approach | Result | Response Time |
|---|---|---|
| Without plugin, global cache | Clears everything on any change | 2 s |
| With plugin, per-page cache | Clears only changed page | 0.4 s |
What's Included in Turnkey Plugin Development
- Plugin source code with documentation
- Composer package setup (optional)
- Example usage of custom methods and hooks
- Migrations (if needed)
- Installation and configuration instructions
- One month of support after deployment
Typical Plugin Development Mistakes
- Improper cache handling — clear only those records that changed.
- Ignoring semantic versioning — breaks compatibility on updates.
- Hardcoding options instead of using
option()— complicates configuration.
Work Process
- Analysis — we study current architecture and requirements.
- Design — define hooks, methods, fields, routes. Agree with you.
- Implementation — write code in PHP 8.3 and Vue 3.
- Testing — unit tests for key scenarios, check on staging.
- Deployment — configure on server, verify caching compatibility.
Estimated Timelines
| Plugin Type | Timelines |
|---|---|
| Simple (methods + hooks) | 1–3 days |
| With custom panel field (Vue) | 3–6 days |
| Complex (Panel section + API + cache) | 1–2 weeks |
We'll give an accurate estimate after analyzing requirements. Order a plugin development — we will prepare a technical specification and offer an optimal solution.







