Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Cms Laravel Package

getkirby/cms

Kirby is a file-based CMS for PHP that stores content in flat files, so no database is needed. It combines an intuitive editing experience with a flexible, developer-friendly API and full customization, making it ideal for fast, secure, bespoke websites and apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Kirby is a flat-file CMS: no database, content lives as text files under content/, and PHP templates render it. The getkirby/cms package is the core application — you almost never start from it directly, you scaffold a project and pull it in.

Minimal setup via Composer

mkdir my-site && cd my-site
composer require getkirby/cms

Then create the entry point:

<?php // index.php
require __DIR__ . '/vendor/autoload.php';

echo (new Kirby\Cms\App())->render();

Create the minimum folder skeleton:

content/          # one folder per page, one .txt per language
site/
  blueprints/     # content model definitions for the Panel
  templates/      # PHP templates per page type
  snippets/       # reusable partials
  config/config.php
media/            # generated assets, must be writable
index.php

Run php -S localhost:8000 (or point Apache/Nginx at the root) and hit /.

Where to look first

  1. site/config/config.php — the single most important file; everything routes through it.
  2. site/blueprints/ — this is your "schema." Define fields here, and the Panel gives you an editing UI for free.
  3. site/templates/default.php — the fallback template; if you see a blank page this is usually why.
  4. The Panel at /panel — needs a user account in site/accounts/, created with vendor/bin/kirby helpers or manually.

First real use case

Model one page type end to end:

site/blueprints/pages/article.yml

title: Article
fields:
  title:
    type: text
  text:
    type: textarea
  cover:
    type: files
    max: 1

site/templates/article.php

<h1><?= $page->title()->html() ?></h1>
<?php if ($image = $page->cover()->toFile()): ?>
  <img src="<?= $image->url() ?>" alt="">
<?php endif ?>
<?= $page->text()->kirbytext() ?>

That loop — blueprint → template → Panel — is the entire day-to-day workflow.

Implementation Patterns

Template/controller/snippet separation. Keep logic out of templates with controllers. A controller for article lives at site/controllers/article.php and returns an array that becomes template variables:

<?php // site/controllers/article.php
return function ($page) {
    return [
        'related' => $page->siblings()->filterBy('template', 'article')->limit(3),
    ];
};

Snippets are the composition primitive: <?php snippet('header') ?>, <?php snippet('card', ['item' => $item]) ?>.

Page models for domain logic. Rather than stuffing helpers into config, extend Page per template:

<?php // site/models/article.php
class ArticlePage extends Kirby\Cms\Page
{
    public function readingTime(): int
    {
        return max(1, (int) ceil(str_word_count($this->text()->value()) / 200));
    }

    public function isPublished(): bool
    {
        return $this->date()->toDate() <= time();
    }
}

Then $page->readingTime() and $page->isPublished() are available in templates — this is the cleanest extension point Kirby offers.

Hooks for cross-cutting concerns. Register in site/config/config.php:

return [
    'hooks' => [
        'page.create:after' => function ($page) {
            // e.g. warm a cache, notify a service, set a UUID
        },
        'file.upload:after' => function ($file) {
            // generate a derivative, strip EXIF
        },
    ],
];

Hooks are your substitute for model callbacks/events. Be careful with page.update:after — it fires often and can cascade if it writes to the same page.

Custom routes / headless JSON. Kirby doubles as a content API without a plugin:

// site/config/config.php
'routes' => [
    [
        'pattern' => 'api/articles',
        'method'  => 'GET',
        'action'  => function () {
            $items = kirby()->pages()->find('blog')->children()->listed();
            return Kirby\Http\Response::json(
                $items->map(fn ($p) => [
                    'title' => $p->title()->value(),
                    'url'   => $p->url(),
                    'text'  => $p->text()->value(),
                ])->values()
            );
        },
    ],
],

For a truly headless setup, combine a null template (site/templates/api.php never rendering HTML) with ->toJson() on collections.

Plugins as the extension boundary. A plugin is a folder in site/plugins/<name>/index.php returning an array of registrations:

<?php // site/plugins/analytics/index.php
Kirby::plugin('my/analytics', [
    'options' => ['token' => null],
    'pageMethods' => [
        'trackView' => function () { /* ... */ },
    ],
    'routes' => [ /* ... */ ],
    'hooks' => [ /* ... */ ],
]);

This is the sanctioned way to add behavior without forking core. Keep vendor-ish plugins in site/plugins/, project-specific logic in models/controllers.

Caching. Enable the pages cache in config and invalidate explicitly:

'cache' => ['pages' => ['active' => true]],

Then kirby()->cache('pages')->flush() after programmatic content writes (CLI imports, migrations, external syncs). Kirby does not know about changes made outside its own write path.

Multi-language. Content files gain a language suffix (article.en.txt, article.de.txt). Templates access $page->text()->value() per current language; use $page->content('de') for cross-language reads. Set 'languages' => true and define languages in the Panel or config.

CLI-ish workflows. Use Kirby in a script with new Kirby\Cms\App(['roots' => [...]]) to batch-import content, regenerate thumbs, or run migrations. This is far more reliable than poking at .txt files with regex.

Gotchas and Tips

The license is the biggest gotcha. NOASSERTION on the repo is accurate: Kirby is not open source. It's source-available under a proprietary license — free for local development and small/hobby sites, but production use on non-trivial sites requires a paid license. Do not assume GPL/MIT compatibility for redistribution. Check the license terms before shipping a product or embedding Kirby in a commercial offering. This alone can disqualify it in some orgs.

kirby/ vs vendor/getkirby/cms/. The downloadable zip ships a kirby/ folder; Composer installs into vendor/getkirby/cms/. Tutorials mix these paths constantly. If a snippet references kirby/bootstrap.php and you installed via Composer, adjust to vendor/autoload.php + Kirby\Cms\App. When in doubt, override roots in config rather than moving folders.

Content file format is not YAML frontmatter. Kirby uses four dashes (----) as the field separator, not ---. Hand-editing is easy to get subtly wrong, and a malformed file often fails silently (field returns empty) rather than throwing. Always validate programmatic writes by re-reading through $page->content()->toArray().

Blank page = missing template or blueprint. Kirby falls back to default.php and will render nothing if it doesn't exist. Check intendedTemplate() vs template() — a page whose blueprint doesn't exist won't behave as expected in the Panel either.

media/ must be writable. The Panel assets, thumbnails, and resized images are generated into media/ and site/cache/. On read-only filesystems or restrictive shared hosting, the Panel appears broken with no obvious error. Set 'thumbs' => ['driver' => 'im'] or ensure GD is installed; otherwise ->thumb() silently returns the original.

Nginx needs real config. The .htaccess shipped with Kirby protects content/, site/, and dotfiles. On Nginx you must replicate those deny rules yourself, or your raw content and blueprints are publicly readable. This is a common production leak.

The pages cache does not invalidate on external changes. Any sync from Git, an importer, or another process leaves stale renders. Flush after writes:

kirby()->cache('pages')->flush();

Also remember the Panel's own cache lives in site/cache/ — deleting that folder is the universal "why is it stale" fix.

Hooks fire during Panel saves too. A page.update:after hook that writes to the page will recurse. Guard with a flag or use page.update:before when you only need the incoming values.

->value() vs ->html() vs raw. Kirby escapes nothing by default with <?= $page->text() ?>. Use ->html() for single-line, ->kirbytext() for rich text, and ->value() when you need unescaped data (e.g., feeding JSON). Mixing these is the most common source of XSS or double-escaped output.

Sorting/filtering returns collections, not arrays. $page->children()->listed()->sortBy('date', 'desc') is chainable and lazy; don't call ->toArray() early, and remember filterBy('template', 'article') matches the template name, not the blueprint title.

Upgrades between majors are breaking. Kirby 4 → 5 changed PHP requirements (8.2+) and namespaced several core classes. Pin with ^4.0 or ^5.0 in composer.json and read the upgrade guide; the kirby: CLI command (vendor/bin/kirby) is sparse, so plan to test the Panel manually after bumping.

Debugging. 'debug' => true in config surfaces Whoops error pages. dump() / dd() are globally available. kirby()->option('x') prints resolved config (useful when you suspect a config file isn't loading — Kirby merges config.php, config.<host>.php, and config.<environment>.php in a specific order).

Don't edit core. Everything in kirby/ or vendor/getkirby/cms/ is overwritten on update. Plugins, models, hooks, and blueprint overrides in site/ are the supported surface — and they're enough for nearly everything.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager