Panel plugins
Some extensions need a user interface. Panel fields, views and blocks are Vue components, which you can write by hand or, once they grow, build from single file components.
Everything so far ran on the server. As soon as your plugin adds a field, a view or a block to the Panel, your plugin needs to provide the necessary parts to render a Panel UI.
This page continues from Your first plugin. It does not depend on Structuring your plugin code, so you can come here directly if the Panel UI is what you need.
Two parts of a Panel extension
Any Panel-related extension consists of two parts: The PHP half declares that the extension exists and prepares its data. The JavaScript half renders it. In its smallest form that is one file each:
site
plugins
anchor
- index.js
- index.php
Kirby loads index.php, index.js and index.css from the plugin root. Nothing else in the folder is loaded unless you tell Kirby to.
The PHP half
Registering a field type looks like any other extension. Anything you put under computed is handed to the Vue component as a prop:
Kirby::plugin(
name: 'superwoman/anchor',
extends: [
'fields' => [
'anchor' => [
// everything that is computed here is handed to the
// `k-anchor-field` Vue component as a prop
'computed' => [
'anchor' => function (): string {
return $this->model()->anchor();
}
]
]
]
]
);
Kirby derives the component name from the extension type and the key: a field registered as anchor looks for a Vue component called k-anchor-field.
The type is now available in any blueprint, and nothing shows up in the Panel until you add it to one:
fields:
anchor:
type: anchor
label: Anchor
The JavaScript half
Write the plugin root's index.js by hand and the component is a plain object, with its markup in a template string:
window.panel.plugin("superwoman/anchor", {
fields: {
anchor: {
props: {
// computed by the PHP part of the field in the plugin's index.php
anchor: String,
// set in the blueprint
label: String
},
template: `
<k-field :label="label" class="k-anchor-field">
<k-code>#{{ anchor }}</k-code>
</k-field>
`
}
}
});
The props it declares are the values that the PHP half computed, plus the ones the blueprint set. k-field is Kirby's own wrapper and takes care of the label.
That is a complete, working field, with no build step and nothing to install. If your extension is small enough to write this way, you can stop here.
A plugin that adds nothing but Panel code still needs an index.php that registers the name. Without it, Kirby loads the JavaScript anonymously and cannot show the plugin's name and version in the system view.
This field only shows the anchor, it never writes to the content file. A field that stores what the user types needs a value prop and an input event on top of this, which Your first Panel field covers step by step.
Single file components
The template above is a string, and a string is all your code editor/IDE sees: no syntax highlighting, no formatting, and nowhere to put styles. A Vue single file component gives you markup, logic and CSS in one .vue file, in exchange for a build step.
The component moves into a file of its own:
<template>
<k-field :label="label" class="k-anchor-field">
<k-code>#{{ anchor }}</k-code>
</k-field>
</template>
<script>
export default {
props: {
// computed by the PHP part of the field in the plugin's index.php
anchor: String,
// set in the blueprint
label: String
}
};
</script>
<style>
.k-anchor-field code {
user-select: all;
}
</style>
src/index.js becomes the entry point and does nothing but register the components:
import AnchorField from "./components/AnchorField.vue";
window.panel.plugin("superwoman/anchor", {
fields: {
anchor: AnchorField
}
});
Which leaves the plugin folder looking like this:
site
plugins
anchor
src
components
- AnchorField.vue
- index.js
- index.js
- index.css
- index.php
- package.json
The two index.js files are not a mistake. src/index.js is the source you edit. The index.js in the plugin root is the compiled bundle that Kirby loads, and index.css next to it is where the component's styles end up.
Building with kirbyup
Single file components cannot run in the browser as they are. We use kirbyup to compile them. It is built for Kirby Panel plugins and needs no configuration.
You do not have to install kirbyup. The commands below fetch it on first use, which may take a moment:
{
"name": "anchor",
"private": true,
"scripts": {
"dev": "npx -y kirbyup src/index.js --watch",
"serve": "npx -y kirbyup serve src/index.js",
"build": "npx -y kirbyup src/index.js"
}
}
Run the commands from your plugin folder:
npm run devcompilessrc/index.jsintoindex.jsandindex.cssand recompiles on every change. Reload the Panel to see the result.npm run servedoes the same, but reloads the Panel for you whenever you save. Pass extra options after--, for examplenpm run serve -- --port 1234.npm run buildcreates the minified files you ship.
Commit the build
Kirby loads index.js and index.css from the plugin root and nothing else. Those two files are build output, so it is tempting to ignore them in Git. Do not.
Only Composer users could rebuild your plugin, and even they have no reason to run npm. Everyone who installs your plugin from a ZIP file or as a Git submodule gets exactly what is in your repository. If the compiled index.js and index.css are missing, your plugin loads without ever showing its interface, and nothing reports an error.
Run npm run build before you tag a release and commit the result. What belongs in .gitignore is the development leftovers:
# npm modules
/node_modules
# kirbyup temp development entry
/index.dev.mjs
What to build next
The reference lists every Panel extension type. Our cookbook walks through the most common ones in detail: