Skip to content

Subpage builder

Auto-generate subpages when you create a new page

If you want to auto-create a given set of subpages every time you create a particular page, you can achieve this with a page.create:after hook. To make it more versatile, you can define a custom blueprint setting that leverages Kirby's new $page->blueprint() method. We will take you step by step through an example based on the Starterkit.

Let's say every time you create a new note page (in the Panel or progamatically), you want to create two subpages: gallery and info.

Blueprint settings

Add the custom option subpage_builder to the note.yml blueprint in the Starterkit:

title: Note
num: date
icon: 📖

  - title: Gallery
    uid: gallery
    template: gallery
    num: 1
  - title: Info
    uid: info
    num: 2
    template: info

  - width: 2/3
        type: textarea
        size: large

  - width: 1/3
        type: pages
          - gallery
          - info
        type: fields
            type: date
            time: true
            default: now
            type: users
          tags: true
            type: pages
            query: site.find("photography").children
            multiple: false
            info: "{{ page.images.count }} image(s)"
            empty: "No gallery selected"
              cover: true
            help: Place the {{ gallery }} tag anywhere in your text to add the selected gallery

As you can see above, we also added a pages section for the subpages.

The hook

Now we need a hook that is called each time a page is created.

We can define it within the return array of our config.php:

'hooks' => [
        'page.create:after' => function ($page) {

Within the hook's callback function, we call a function called buildPageTree() with the newly created $page as first parameter. We will define that function in a plugin.

The plugin

The buildPageTree() function is a recursive function that builds new subpages as long as the blueprint has a subpage builder setting with valid values.


function buildPageTree($page) {

    // get the subpage_builder definition from the blueprint
    $builder = $page->blueprint()->subpage_builder();

    // if any is set…
    if (empty($builder) === false) {

        // …loop through each page to be built
        foreach($builder as $build) {

            // check if all necessary fields have been defined
            // in subpage_builder
            $missing = A::missing($build, ['title', 'template', 'uid']);

            // if any is missing, skip
            if (empty($missing) === false) {

            try {
                // the parent itself is created as a draft
                $subPage = $page->createChild([
                    'content'  => [
                        'title' => $build['title']
                    'slug'     => $build['uid'],
                    'template' => $build['template'],
            } catch (Exception $error) {
                throw new Exception($error);

            // publish the subpages, so that they are published
            // when the parent is published
            if ($subPage) {

                // call the function recursively

                // publish subpages and sort

                if (isset($build['num']) === true) {

In this example, all subpages in the tree are automatically published. That way, they will be published together with the parent without any further interaction.