Skip to content

kirbyuse ​

kirbyuse adds the Kirby-specific layer on top of Vue: types for window.panel, composables for common Panel tasks and prop sets for sections, fields and blocks.

Versions

kirbyuse 1 pairs with kirbyup 3 and Kirby 4 or 5.

Installation ​

bash
pnpm add -D kirbyuse
bash
npm install -D kirbyuse
bash
yarn add -D kirbyuse

Imports ​

Kirby 4 and 5 load Vue 2 as a global script without named exports, so import { ref } from 'vue' has nothing to resolve to. kirbyuse re-exports the Composition API, so everything comes from one package:

js
import { computed, ref, usePanel, useSection } from 'kirbyuse'

At runtime, the exports point at the Panel's own Vue. Your plugin and the Panel share one Vue.

Panel Types ​

usePanel returns window.panel with types for every service:

js
import { usePanel } from 'kirbyuse'

const panel = usePanel()

panel.notification.success('Saved')
panel.dialog.open({ /* ... */ })
panel.view.reload()

To type the global directly, import the package once in the entry file:

js
// src/index.js
import 'kirbyuse'

window.panel.notification.success('Types work')
//           ^? (property) notification: PanelNotification

No TypeScript required, the hints work in JavaScript files as well.

Composables ​

useSection ​

Loads the section data Kirby prepared on the server. Together with the section prop set this is all a custom section needs:

vue
<!-- src/components/DemoSection.vue -->
<script>
export default {
  inheritAttrs: false,
}
</script>

<script setup>
import { ref, useSection } from 'kirbyuse'
import { section } from 'kirbyuse/props'

const props = defineProps({ ...section })

const label = ref('')
const items = ref([])

const { load } = useSection()

async function loadSection() {
  const response = await load({
    parent: props.parent,
    name: props.name,
  })
  label.value = response.label
  items.value = response.items
}

loadSection()
</script>

<template>
  <k-section :label="label">
    <k-items :items="items" />
  </k-section>
</template>

useContent ​

Reactive access to the content of the current view:

js
import { useContent, watch } from 'kirbyuse'

const { currentContent, contentChanges, hasChanges, update } = useContent()

watch(hasChanges, (changed) => {
  if (changed)
    console.log('Unsaved changes')
})

update({ title: 'New title' })

currentContent reflects the editor state including unsaved changes, contentChanges holds only the diff and update writes into it. Kirby 4 and 5 store content differently, the composable hides that.

useDialog ​

Opens Kirby's dialogs and resolves with the result:

js
import { useDialog } from 'kirbyuse'

const { openTextDialog, openFieldsDialog } = useDialog()

const confirmed = await openTextDialog('Delete this entry?')

const result = await openFieldsDialog({
  fields: {
    email: { type: 'email', label: 'Email' },
  },
})
// result: { email: '...' } or undefined when cancelled

openFieldsDialog takes an onSubmit handler to validate before the dialog closes. Return false to keep it open.

useI18n ​

Translates the objects Kirby uses for labels:

js
import { useI18n } from 'kirbyuse'

const { t } = useI18n()

t({ en: 'Hello', de: 'Hallo' }) // Follows the Panel language

For Kirby's own translation strings, panel.t() is the right call.

All Composables ​

ComposableReturns
usePanelThe typed window.panel
useApipanel.api for requests to the backend
useApppanel.app, the Vue application instance
useSection{ load }
useContent{ content, currentContent, contentChanges, hasChanges, update }
useDialog{ openTextDialog, openFieldsDialog }
useBlock{ field, open, update } for custom block components
useI18n{ t }
useHelpersKirby's $helper utilities
useLibraryKirby's $library bundle, such as dayjs
useStoreThe Vuex store, Kirby 4 only

Prop Sets ​

kirbyuse/props exports the props Kirby passes to a component, so you spread them instead of copying them:

js
import { section } from 'kirbyuse/props'

defineProps({ ...section })

The individual field props such as label, disabled and required are available as well.

Plugin Assets ​

For assets that should load on demand, register them once and resolve them by file name:

HelperPurpose
registerPluginAssets(assets)Registers { filename, url } pairs, usually from a PHP-side asset list
resolvePluginAsset(filename)Returns the registered asset
loadPluginModule(filename)Imports a registered JavaScript module, cached