Context menu - how to get a current branch from the whole menu tree?


I’m new here so welcome everybody. First I have to admit I’m not a programmer. I’m rather a kinda old-school webmaster, doing a bit of everything, but never managed to really learn PHP.

Grav is my next CMS of choice, after I realized the WordPress is going the wrong way. For a couple of months I’ve managed to explore solutions for my typical problems myself, but right now I got stuck.

I have a web site with 3 levels of navigation (I’m doing a silo type of navigation). Main menu was no problem as it just displays all of the pages. But I have a problem with context submenu on subpages (additional menu - so a user doesn’t has to use a complex main menu to find pages within one level of navigation).

What I need is simply to show 1st level page’s children on every page of that level (with active indicator). So from the whole tree of navigation I need just one branch. Problem is I don’t know how to do it with twig.

I’m building my custom theme, I have ‘borrowed’ the main menu code from default Quark theme.

Thanks in advance if anyone might help me.


@blazejs, Before building anything that might not reflect your goals, would you mind being a bit more specific?

Considering the page tree below:

  • what should be shown on page that has no children (home, child1, grandchild1/2)
  • what should be shown on page that has children (parent, child2)
├── 01.home
│   └──
└── 02.parent
    ├── 01.child1
    │   └──
    ├── 02.child2
    │   ├── 01.grandchild1
    │   │   └──
    │   ├── 02.grandchild2
    │   │   └──
    │   └──

You said:

with active indicator

If a page shows a list of children, none of the children can be active. Isn’t it?

Hello and thank you for your reply

I want that menu to be placed in the sidebar. So if a page has no children I will display there a text block.

By ‘active indicator’ I mean adding a class ‘active’ so I can style the current page link, depending on which child or grandchild page is currently displayed.

What I need is:

  • if I’m on the e.g. 02.parent page that has the children (and grandchildren) I want to show only the part of the whole tree that relates to this page (if a page has no children I’ll show a text block)
  • I want to have that menu on parent page, its children and grandchildren (but without the first level parent page - it’s always visible in the main menu). I will use just a nested list, so it may be the same on every page within the current branch (even better, user might browse the whole branch without the need to move back to the higher level page)
  • well, to show directly what I need I have to show a sample… it’s my very old project made on WordPress: Packages | Bobrowy Dwór - you’ll see a brown submenu on the left (looks like an open book). That’s exactly what I want to achieve. I may show also the real project we’re talking about but it’s on production server and unfinished.

@blazejs, I’m afraid that didn’t help me much… Hopefully someone else will jump in.

I believe children and descendants collections should help you there

In this case, can’t find any method to get all ancestors, so probably will need to impelement your own logic using parent

I see book shows siblings there (you’ll have to include current page yourself, because siblings are just siblings; Or get parent and then all parents children, which will include current page too)

Hello and thank you for your responses

Well, the trial and errors method I have managed to do something like this: Hurtownia, sklep klimatyzacja - Czechowice-Dziedzice, Bielsko-Biała, klimatyzatory i akcesoria

This is the top level page with the biggest amount of subpages…

My code looks like this:

In macros.html.twig:

{% macro contextmenu(page) %}
  {% import _self as macro2 %}
  {% for i in page.children.visible %}
  {% set current_child = ( or i.activeChild) ? 'is-active' : '' %}
  <li class="context-menu__item {{ current_child }}">
    <a href="{{ i.url }}">{{ i.title }}</a>
      {% if i.children.visible.count > 0 %}
      <ul class="submenu">
        {{ macro2.contextmenu(i) }}
      {% endif %}
  {% endfor %}
{% endmacro %}

In default.html.twig:

{% import 'macros/macros.html.twig' as macro2 %}
<ul class="context-menu">
{{ macro2.contextmenu(page) }}

On top lebel (page from the link) it looks like I’d like to to be. But on the children pages it shows just the children of the current page. I know it’s quite logical. I have figured out that the clue is somewhere in this: {{ macro2.contextmenu(page) }}. If I use “pages” it iterates through all the pages of the site (like in top menu), if I use “page” it iterates only through the current page. Is it possible to iterate through the whole node (so e.g. starting with the page “klimatyzacja”) even if I’m on the lowest child? I guess such a context menu is a common pattern, so there must be a way to get this.

I used to work with the Get Simple CMS which is also a flat file CMS. There is the “i18n” plugin that also creates the menues (but you have to define the structure of a menu first). With that plugin I was able to display the menu starting on any level I wanted. So if I wanted to get such a submenu I just started on second level - and I got what I wanted.

As I said, you’ll have to implement it with page.parent. Just do a macro same way you did for children

How can I get the top level parent?

Generally I was trying something that, but on the top level page it was displaying also full top level navigation.

If you were to rename i to page, you’d see you are getting page.children of a children of a children… recursively. Exact same with parent - get a page.parent of a parent of a parent… Of course your list will become reversed, so you’ll need a way to reverse it again

@blazejs, To get a complete page tree on every descendant of the root of the current page, try the following:

  • Using latest Grav + Quark
  • Copy /user/themes/quark/templates/partials/navigation.html.twig to /user/themes/quark/templates/partials/context-nav.html.twig
  • Change the content of /user/themes/quark/templates/partials/context-nav.html.twig into:
    {% import 'macros/macros.html.twig' as macros %}
    {% set toplevelPage = pages.find('/'~uri.paths[0]) %}
    <ul {{ tree ? 'class="tree"' : '' }}>
      {{ macros.nav_loop(toplevelPage) }}
  • Update /user/themes/quark/templates/default.html.twig into:
    {% extends 'partials/base.html.twig' %}
    {% block content %}
       {% include 'partials/context-nav.html.twig' %}
       {{ page.content|raw }}
    {% endblock %}
  • The result should now be something like:

If I get it right, if Child1 had children and you were on Child2 > Grandchild2, there shouldn’t be neither Child1 nor its children listed, but in your example there will be. Or I misunderstood both the OP question and your example? :slight_smile:


If I get it right […] Or I misunderstood both the OP question and your example?

Oh well, probably I am mistaken, hence my cry for clarity…

Maybe things would be more clear if OP showed something like:

  • On /home display: none
  • On /parent display: …
  • On /parent/child1 display: …

Thank you very, very much!

This is exactly what I wanted to get. This one line made the difference:

The number in brackets ([0]) sets the level of navigation, right?


This is exactly what I wanted to get.

If I knew what you need, I might agree, but doubts arise because of the following question…

The number in brackets ([0]) sets the level of navigation, right?

The number in brackets doesn’t set anything, it only returns the nth element in the array of path segments returned by uri.paths()

Anyway it works as I intended to. Thank you very much again.

You see, I’m not a programmer but I used to work with many, so I know there might be misunderstandings because we think on different levels of abstraction :slight_smile: I’m mostly working with HTML+CSS and design, but I know quite enough to build a web site myself with the help of documentation (if written well) and some snippets.
I knew about Grav for a few years, but at the first look it was too difficult for me. In the meanwhile I’ve tried Middleman static site generator and so ERB templating seems quite similar to TWIG, what helped me a lot. I did my first Grav web site just a few months ago. I was looking for an alternative to the WordPress which is becoming a page builder (I used to love it earlier, now I hate it), and I prefer to have some control over the code…