Menus
In Timber, you can use Timber\Menu
to make a standard WordPress menu available to the Twig template as an object you can loop through.
Once the menu becomes available to the context, you can get items from it in a way that is a little smoother and more versatile than WordPress’s wp_nav_menu()
function. (And you never again need to rely on a crazy Walker Function).
Initializing a menu #
Timber\Menu
can be initialized with different values:
You can pass the slug of the menu you want to use:
$menu = new Timber\Menu( 'site-tools' );
Or the ID number of the menu:
$menu = new Timber\Menu( 3 );
Or the proper name from the admin:
$menu = new Timber\Menu( 'Primary Navigation' );
Or the slug of the registered location:
$menu = new Timber\Menu( 'primary' );
Or pass nothing. This is good if you have only one menu. In that case Timber will just grab what you got.
$menu = new Timber\Menu();
Options #
Optionally, you can send additional options to Timber\Menu
. Current only depth
is supported (see https://developer.wordpress.org/reference/functions/wp_nav_menu/ for reference)
$args = array(
'depth' => 2,
);
$menu = new Timber\Menu( 'primary', $args );
Setting up a menu globally #
The first thing to do is to initialize your menu using Timber\Menu
. This will make the menu available as an object to work with in the context. Because we need the menu on every page, we can add it to the global context through the timber/context
filter:
functions.php
<?php
add_filter( 'timber/context', 'add_to_context' );
function add_to_context( $context ) {
// So here you are adding data to Timber's context object, i.e...
$context['foo'] = 'I am some other typical value set in your functions.php file, unrelated to the menu';
// Now, in similar fashion, you add a Timber Menu and send it along to the context.
$context['menu'] = new \Timber\Menu( 'primary-menu' );
return $context;
}
Now, when you call Timber::context()
, your menu will already be set in the context. You don’t need to initialize the menu in all your template files.
index.php
<?php
$context = Timber::context();
Timber::render( 'index.twig', $context );
When adding multiple menus to the global $context
, declare unique names for each menu in the add_to_context
function.
functions.php
public function add_to_context( $context ) {
$context['primary_menu'] = new Timber\Menu('Primary Navigation');
$context['footer_menu'] = new Timber\Menu('Footer Navigation');
// ...
return $context;
}
Displaying the menu items #
In your Twig file, you can loop over the menu items like normal arrays. You’re in complete control of the markup.
index.twig
<nav>
<ul class="nav-main">
{% for item in menu.items %}
<li class="nav-main-item {{ item.classes|join(' ') }}">
<a class="nav-main-link" href="{{ item.link }}">{{ item.title }}</a>
{% if item.children %}
<ul class="nav-drop">
{% for child in item.children %}
<li class="nav-drop-item">
<a href="{{ child.link }}">{{ child.title }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
</nav>
When using multiple menus, use the unique name in your Twig markup. Notice primary_menu.items
instead of menu.items
.
index.twig
<nav>
<ul class="nav-main">
{% for item in primary_menu.items %}
<li class="nav-main-item {{ item.classes|join(' ') }}">
<a class="nav-main-link" href="{{ item.link }}">{{ item.title }}</a>
<!-- ... -->
</li>
{% endfor %}
</ul>
</nav>
The current menu item #
When you need to check whether a menu item is the current menu item, you can use the current
property. Here’s an example to display child menu items only if it’s the menu item is the currently visited page:
Twig
{% if item.current and item.children %}
<ul class="nav-child">
{% for child in item.children %}
<li class="nav-child-item">
<a class="nav-child-link" href="{{ child.link }}">{{ child.title }}</a>
</li>
{% endfor %}
</ul>
{% endif %}
Other properties that are available are current_item_parent
for direct parents of a menu item and current_item_ancestor
for when you have deeper nesting.