Skip to content

Kirby 3.9.8

Menus

<?php

// main menu items
$items = $pages->listed();

// only show the menu if items are available
if($items->isNotEmpty()):

?>
<nav>
  <ul>
    <?php foreach($items as $item): ?>
    <li><a<?php e($item->isOpen(), ' class="active"') ?> href="<?= $item->url() ?>"><?= $item->title()->html() ?></a></li>
    <?php endforeach ?>
  </ul>
</nav>
<?php endif ?>
<?php

$items = false;

// get the open item on the first level
if($root = $pages->findOpen()) {

  // get visible children for the root item
  $items = $root->children()->listed();
}

// only show the menu if items are available
if($items and $items->isNotEmpty()):

?>
<nav>
  <ul>
    <?php foreach($items as $item): ?>
    <li><a<?php e($item->isOpen(), ' class="active"') ?> href="<?= $item->url() ?>"><?= $item->title()->html() ?></a></li>
    <?php endforeach ?>
  </ul>
</nav>
<?php endif ?>
<?php

// independent sub sub menu
$items = false;

// get the open item on the first level
if($root1 = $pages->findBy('isOpen', true)) {

  // get the open item on the second level
  if($root2 = $root1->children()->findOpen()) {

    // get visible children of the second level item
    $items = $root2->children()->listed();
  }
}

// only show the menu if items are available
if($items and $items->isNotEmpty()):

?>
<nav>
  <ul>
    <?php foreach($items as $item): ?>
    <li><a<?php e($item->isOpen(), ' class="active"') ?> href="<?= $item->url() ?>"><?= $item->title()->html() ?></a></li>
    <?php endforeach ?>
  </ul>
</nav>
<?php endif ?>

Tree menu

The recursive treemenu snippet renders a menu of all your site's pages and subpages.

<?php if(!isset($subpages)) $subpages = $site->children() ?>
<ul>
  <?php foreach($subpages->listed() as $p): ?>
  <li class="depth-<?= $p->depth() ?>">
    <a<?php e($p->isActive(), ' class="active"') ?> href="<?= $p->url() ?>"><?= $p->title()->html() ?></a>
    <?php if($p->hasChildren()): ?>
    <?php snippet('treemenu', ['subpages' => $p->children()]) ?>
    <?php endif ?>
  </li>
  <?php endforeach ?>
</ul>

Usage

Save the code as treemenu.php in the /site/snippets folder. Saving it as a snippet and with this exact name is key in this case. If you just include the code in your template or if you rename the snippet, it won't work, because the snippet is called from within the snippet.

Include it in your code using the snippet() helper:

<?php snippet('treemenu') ?>

If you don't want it to start from the first level, pass it a set of pages, where it should start. i.e.:

<?php

// get the first set of subpages which should be used
$subpages = $pages->find('about-us')->children();

// create the snippet beginning with those subpages
snippet('treemenu', ['subpages' => $subpages]);

?>

Nested menu

<?php

// nested menu
$items = $pages->listed();

// only show the menu if items are available
if($items->isNotEmpty()):

?>
<nav>
  <ul>
    <?php foreach($items as $item): ?>
    <li>
      <a<?php e($item->isOpen(), ' class="active"') ?> href="<?= $item->url() ?>"><?= $item->title()->html() ?></a>

      <?php

      // get all children for the current menu item
      $children = $item->children()->listed();

      // display the submenu if children are available
      if($children->isNotEmpty()):

      ?>
      <ul>
        <?php foreach($children as $child): ?>
        <li><a<?php e($child->isOpen(), ' class="active"') ?> href="<?= $child->url() ?>"><?= $child->title()->html() ?></a></li>
        <?php endforeach ?>
      </ul>
      <?php endif ?>

    </li>
    <?php endforeach ?>
  </ul>
</nav>
<?php endif ?>

Custom menu

<?php

// selective items

$items = $pages->find('terms-of-service', 'faq', 'support');

if($items and $items->isNotEmpty()):

?>
<nav>
  <ul>
    <?php foreach($items as $item): ?>
    <li><a<?php e($item->isOpen(), ' class="active"') ?> href="<?= $item->url() ?>"><?= $item->title()->html() ?></a></li>
    <?php endforeach ?>
  </ul>
</nav>
<?php endif ?>

Selectbox menu

<?php

// main menu items
$items = $pages->listed();

// only show the menu if items are available
if($items->isNotEmpty()):

?>
<select onchange="window.location.href = this.value">
  <?php foreach($items as $item): ?>
  <option value="<?= $item->url() ?>"<?php e($item->isOpen(), ' selected="selected"') ?>><?= $item->title()->html() ?></option>
  <?php endforeach ?>
</select>
<?php endif ?>
<nav aria-label="breadcrumb">
  <ol>
    <?php foreach($site->breadcrumb() as $crumb): ?>
    <li<?php e($crumb->isActive(), ' aria-current="location"') ?>><a href="<?= $crumb->url() ?>"><?= $crumb->title()->html() ?></a></li>
    <?php endforeach; ?>
  </ol>
</nav>

Styling and markup

All the examples above are pretty raw. You probably want to add additional classes or more markup to the list elements to style them. That's all up to you. Here are two more little helpers, which will make your life easier:

Dedicated CSS selectors for individual pages

It's often necessary to style particular items in a menu without touching the others – for example if you want to add icons. This can be done by adding dedicated selectors:

In your template

<nav>
  …
  <li class="<?= $item->uid() ?>"><a<?php e($item->isOpen(), ' class="active"') ?> href="<?= $item->url() ?>"><?= $item->title()->html() ?></a></li>
  …
</nav>

In your CSS

nav .home {
  background: url(../images/home.svg) no-repeat left center;
}
nav .contact {
   background: url(../images/contact.svg) no-repeat left center;
}

The uid() method displays the folder name of each item without the prepended number.

Note that the UID is subject to change if the user is allowed to change it. It might therefore make sense to either disallow changing of the UID or use an identifier that may not be changed.

Page Depth

Especially in nested menus it can be helpful to have an additional selector that indicates the depth of the item, for example for indenting list items.

In your template

<nav>
  …
  <li class="depth-<?= $item->depth() ?>"><a<?php e($item->isOpen(), ' class="active"') ?> href="<?= $item->url() ?>"><?= $item->title()->html() ?></a></li>
  …
</nav>

In your CSS

nav .depth-2 {
  padding-left: 20px;
}

The entire API

Of course this is only a minimal extract of all possibilities. The entire Kirby API is there to get the most out of your ideas, so check out the Kirby Reference for additional methods you can use.