medienbaecker/kirby-modules

Easily add modules to your pages

Maintainers

Package info

github.com/medienbaecker/kirby-modules

Type:kirby-plugin

pkg:composer/medienbaecker/kirby-modules

Statistics

Installs: 5 393

Dependents: 1

Suggesters: 0

Stars: 88

Open Issues: 2

5.0.0-rc.8 2026-05-14 14:48 UTC

README

Modular page building for Kirby using regular Kirby pages with their own blueprint and snippet, edited inline on the parent page.

Screenshot of the modules section with two modules, a text module with a textarea and a text with buttons module with both a textarea and a structure field for buttons

Licensing

Kirby Modules is a commercial plugin. You can use it for free on local environments but using it in production requires a valid licence. You can pay what you want, the suggested price being 99€ per project. Feel free to choose "0" when working on a purposeful project ❤️

Buy a licence

Features

  • Edit module fields inline on the parent page with a blocks-like UI
  • Signed previews for hidden modules
  • Great performance with large numbers of modules
  • Robust multilanguage behaviour
  • Automatic container page creation, separating modules from regular subpages
  • Multiple modules sections per page
  • Sensible defaults in module blueprints

Installation

composer require medienbaecker/kirby-modules

Or download this repository and put it into site/plugins/kirby-modules.

What's a Module?

A module is a regular page, differentiated from other pages by being inside a modules container. This makes it possible to use pages as modules without sacrificing regular subpages.

Page
├── Subpage A
├── Subpage B
└── Modules
    ├── Module A
    └── Module B

Quick Start

Add a (or multiple) modules section to your page blueprint:

# site/blueprints/pages/default.yml
title: Default Page
sections:
  modules:
    type: modules

Create a module blueprint and snippet:

# site/blueprints/modules/text.yml
title: Text
fields:
  textarea:
    label: Text
// site/snippets/modules/text.php
<div id="<?= $module->slug() ?>">
  <?= $module->textarea()->kt() ?>
</div>

Or create both files using the CLI command:

kirby make:module gallery

In your snippet, $module is the module page and $page is the parent page. Variables from controllers are also available.

Module blueprints support the full Kirby blueprint layout, including columns and sections:

# site/blueprints/modules/images.yml
title: Images
icon: images
columns:
  - width: 1/2
    fields:
      title:
        label: Title
        type: text
      images:
        label: Images
        type: files
  - width: 1/2
    sections:
      files: true

Render in your template:

// site/templates/default.php
<?= $page->modules() ?>

Anchor Links

Use $module->slug() as the element ID in your module snippet:

<div id="<?= $module->slug() ?>">

The slug is editable in the Panel via the #anchor button on each module card or the "Change anchor" button in the toolbar's dropdown.

Visibility

Each module's visibility can be toggled with a single click on its card. Hidden modules stay in place — they keep their sort position and any inline edits — but the frontend skips over them when iterating $page->modules(). The card shows a striped background while a module is hidden.

Hidden modules get a signed preview URL (token + _module query param) so authors can verify them on the live URL without Panel login. The preview button appears in the card's toolbar only while the module is hidden — visible modules render via their parent's URL.

Section Options

Option Type Description
default string First/pre-selected module type in create dialog
templates array Manually define available types instead of all
templatesIgnore array Hide specific module types
min int Minimum number of modules
max int Maximum number of modules
empty string Empty state text

Multiple Sections

Each section's name (YAML key) becomes the container slug:

sections:
  modules:
    type: modules
    default: text
  sidebar:
    type: modules
    templates:
      - module.cta
      - module.newsletter
// Default container for modules section called `modules`
<?= $page->modules() ?>

// Secondary container for modules section called `sidebar`
<?= $page->modules('sidebar') ?>

File Pools

By default, a files field in a module sees only that module's own files. That's okay if you want to add a section to the module, too. Most of the times, you want to use the (grand)parent page's file pool however.

The filePool method resolves to the right files collection regardless of where it's called:

  • On a module, returns the host page's files (the module's grandparent — the page that owns the modules container).
  • On any other page, the page's own files.
  • On the site, file, or user, that model's own files.

Use it as the query of any files field that should follow this rule:

type: files
query: model.filePool
uploads:
  parent: model.filePool.parent

To access the page of the file pool, you can use model.filePool.parent, as shown in the uploads option.

Config Options

// site/config/config.php
return [
  // Auto-publish new modules (default: false → created hidden). Set true to create visible.
  'medienbaecker.modules.autopublish' => true,
];

Template Methods

Method Description
$page->modules() All modules (default container)
$page->modules('sidebar') Modules from named container
$page->hasModules() Page has modules
$page->isModule() Page is a module
$page->filePool() Files for blueprint queries (host page if module, else self)
$module->moduleId() CSS class (e.g. module--text)
$module->moduleName() Blueprint title

Custom Models

Override the model for all module types via config:

// site/config/config.php
'medienbaecker.modules.model' => CustomModulePage::class,

Or override a single module type via site/models/ (as you would with any regular page):

// site/models/module.text.php
class ModuletextPage extends Medienbaecker\Modules\ModulePage {
  // your methods
}