Introduction to Kirby CMS

Lukas White

Kirby is a lightweight CMS that probably fits somewhere between feature-rich platforms such as Drupal, and static site generators such as Jekyll.

What makes Kirby quite different to most CMS’s – and closer to Jekyll in the process – is that it has no database. Instead, everything is stored as files; some for configuration, some for content – all in addition to the usual template files, partials and plugins.

In this article I’m going to take a look at Kirby, demonstrate how to use it, and assess some of its strengths and weaknesses.

Licensing, Downloading and Installing

Kirby is provided on the basis of a per-site license of (at time of writing) $39 USD. However, you can try it out on your local machine for free – it’s only when you get into production that the license fee becomes payable.

The package is available from the Kirby Downloads page as a .zip file, or from Github.

To install, unzip / copy the archive to your web root and point your virtual host at Kirby’s root directory. And that’s pretty much it – right away, you should see some dummy content and Kirby’s default theme.

The Default Kirby theme

Kirby’s Site Structure

Let’s suppose we plan to work with the basic site structure provided by default, but add a new page at the top-level called “Our Work”. We want it to appear before “Contact” in the menu.

Start by renaming the directory which holds the contact page from content/03-contact to content/05-contact.

By default, the menu system uses the directory name to determine the order pages should appear in the navigation. We’ll create a new page for the fourth position later.

Now, create a new folder called content/03-our-work, and in that a file called our-work.txt

Content files should always have a .txt extension by default, despite their content being a mixture of YAML and Markdown. To modify this behaviour, change the following line in site/config/config.php:

c::set('content.file.extension', 'txt');    

The file should have the following structure:

Title: Our Work
Text: Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor.

Now, if you browse to the site you should see a new menu item in between Projects and Contact, with the content we just defined in 04-our-work/our-work.txt.

Obviously, you can edit existing pages in the same way. You’ll find the contents of the homepage in content/home/home.txt.

Let’s take a look at how the menu is built to get an insight into what’s going on. The menu is implemented as a snippet, which is basically a view partial:

// site/snippets/menu.php

<nav class="menu">
    <?php foreach($pages->visible() AS $p): ?>
    <li><a<?php echo ($p->isOpen()) ? ' class="active"' : '' ?> href="<?php echo $p->url() ?>"><?php echo html($p->title()) ?></a></li>
    <?php endforeach ?>

Notice that it calls a method called visible(). Content prefixed with a number – 01, 02 etc – is visible; that is, it’s designed to appear in the navigation. Content without – such as home and error – is hidden. Try creating a folder called content/test, with a corresponding content file called test.txt – you’ll find that it doesn’t appear on the menu. If you want to hide additional pages, change the following line in site/config/config.php:

c::set('content.file.ignore', array());

$pages->visible() returns pages ordered by directory name, hence the numeric prefixes. You can override that behaviour; suppose you wanted a list of pages alphabetically ordered by title:

$a_to_z = $pages->visible()->sortBy('title', 'asc');

To list pages beneath the currently open menu item (i.e., a sub-menu) you can do this:

if($root = $pages->findOpen()) {
$items = $root->children()->visible();    

There’s lots more information on managing menus on the Kirby website.


In a new installation, pages are rendered using site/templates/default.php.

Let’s take a look at the contents:

<?php snippet('header') ?>
<?php snippet('menu') ?>
<?php snippet('submenu') ?>

<section class="content">

    <h1><?php echo html($page->title()) ?></h1>
    <h2><?php echo html($page->subtitle()) ?></h2>
    <?php echo kirbytext($page->text()) ?>


<?php snippet('footer') ?>

snippet() includes a file from site/snippets – we’ll look at those shortly.

You’ll notice that the page content is available as a variable called $page, which reads the properties defined in the corresponding text file. The html() function is used to escape values, and kirbytext() applies some additional interpretation, as we’ll see shortly.

To create a new template for a specific page, simply create a new file in site/templates whose name matches the filename of the content for the page in question. So, for example, if you want a separate page template for the homepage, you’d create a file named site/templates/home.php. For the “Our Work” page we created earlier, you’d call it our-work.php.

By default, with Kirby comes an assets folder, containing images and styles sub-folders, but you’re free to structure your theme’s assets as you wish.

Site Properties, and Snippets

If you open up the file content/site.txt, you’ll see a bunch of properties of the site itself such as the title, author and copyright notice. You’re free to add additional properties. For example, if you add the following:


Twitter: myusername

You can now print this out in your templates or snippets, using a magic method of the $site variable. So to put it in the footer, open up site/snippets/footer.php and add the following:

<a href="<?php print $site->twitter() ?>">Twitter</a>

Creating a Simple Blog

We’ve seen how to create content, create a page-specific template and generate a list of pages. Armed with this knowledge, creating a simple blog is pretty straightforward.

Create a folder called content/04-blog and in it, a file called blog.txt:

Title: Blog

Within that folder, create a bunch of blog posts (using the same format as the existing pages), e.g.


Now, create a template for the Blog:

// site/templates/blog.php

<?php snippet('header') ?>
<?php snippet('menu') ?>

<section class="content">

    <h1><?php echo html($page->title()) ?></h1>

    <?php foreach ($pages->findOpen()->children()->visible()->flip() as $post): ?>
    <h2><a href="<?php print $post->url() ?>"><?php echo html($post->title()) ?></a></h2>
    <p><?php print excerpt($post->text()) ?></p>
    <p><a href="<?php print $post->url() ?>">Read more &raquo;</a></p>
    <?php endforeach ?>


<?php snippet('footer') ?>

Notice how we’re calling flip() to reverse the order of the pages, all of whom are children of the current page – i.e., pages within the content/04-blog folder.

This template also introduces the url() function, which returns a URL to the page in question, and excerpt(), which returns a summary of the text content. To change the number of characters returned, simply provide it as a second parameter:

print excerpt($post->text(), 250); // prints the first 250 characters


The actual content in Kirby is written in “Kirbytext”. It’s stored as the text property in a YAML file, and is essentially an extension of Markdown but with some additional macros.

This is best illustrated with an example:

File: page.txt:

Title: About
Somefield: Something
Body: This is some sample content. It's based on Markdown, so we can use **bold** or *emphasis*, and [write links](

Kirbytext also has macros, that look a little like this:

(youtube: width: 300 height: 200)

The Kirby-specific macro / tag here is for embedding a Youtube video, but you can extend Kirbytext to write your own. To do so, start by creating a file called kirbytext.extended.php in site/plugins:

class kirbytextExtended extends kirbytext {

function __construct($text, $markdown=true) {

    parent::__construct($text, $markdown);



Now, in the constructor you can define new tags, as well as new attributes. Suppose we wanted to add Gravatar support:

    // define custom tags

    // define custom attributes

Once you’ve defined a new tag through addTags(), you simply implement a method with the same name, which will be called in order to render the tag:

*  Render the gravatar tag, e.g. (gravatar:
function gravatar($params) {

    $email = $params['gravatar'];

    $size = (isset($params['size'])) ? $params['size'] : 50;

    $url = sprintf('', md5($email), $size);

    return sprintf('<img src="%s" width="%d" height="%d" />', $url, $size, $size);


You’d use this as follows:

(gravatar: size: 64)

Note that the email attribute here is named gravatar; i.e., the same as the tag.


There are a number of plugins available on Github including support for Twitter, Instagram and Github, as well as things like contact forms, estimating reading time and displaying related pages.

You can create your own plugins with little to no knowledge of Kirby’s inner workings – simply drop a class into site/plugins and use it from your templates as required.

Kirby Panel

Kirby doesn’t actually provide any sort of administrative interface out-the-box – you simply edit the files directly. However, there is a simple interface available as a separate package, called Kirby Panel.

The Kirby Panel

To install Kirby Panel, clone it from Github.

Then, copy the folder to the site folder of your Kirby site, naming it panel, e.g.:

cp -R kirbycms-panel/ /var/www/yoursite/site/panel

Now copy the panel\defaults folder and place it in the site root:

cp -R /var/www/yoursite/panel/defaults /var/www/yoursite/panel

To specify some login credentials, rename the file site/panel/accounts/admin.php to [username].php, e.g.:

// site/panel/accounts/john.php

<?php if(!defined('KIRBY')) exit ?>
username: john
password: 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
encrypt: sha1
language: en

You can enter a plaintext password (a very, very bad idea), an MD5 hash (a very bad idea) or an SHA1 hash (marginally better) – you just need to explicitly specify which you’re using, or leave it out for plaintext.

Now, if you open http://yoursite.local/panel in your web browser, you’ll have access to a simple administrative interface. This allows you to modify site properties, as well as add / edit / delete content.

Pros and Cons

Let’s look briefly at some of Kirby’s pros and cons.


  • Extremely simple to install
  • Fast
  • Results in clean markup
  • Very shallow learning curve
  • Very little bloat


  • Some of the code tries to reinvent the wheel, for example the device detection
  • Everything sits in the public root, which isn’t particularly logical
  • Dynamic elements such as search are limited at best


I’ve given you a brief introduction of Kirby, but there’s plenty more to explore – check out the documentation and tutorials, find support in the forums, or delve into the source-code. In particular, take a look at kirby/lib/kirby.php, along with the other files in kirby/lib.

What do you think of Kirby, and what might you use it for? Have you already used it in production? Let me know in the comments.