# Submenu navigation for category pages

**Date:** 2020-12-26  
**Author:** Kees C. Bakker  
**Categories:** PHP, WordPress  
**Original:** https://keestalkstech.com/submenu-navigation-for-category-pages/

![Example of a menu on the wall of a diner.](https://keestalkstech.com/wp-content/uploads/2020/12/sunrise-photos-5AMSZcgN_cM-unsplash-scaled.jpg)

---

In WordPress you have two main taxonomies: categories and tags. I use categories as a taxonomy tree. That is why I want to show the submenu on the category page. It does not come out of the box, so I created something that renders the submenu items for me.

![A screenshot of the Automation category showing the title, description, feature image and a list of sub menu links.](https://keestalkstech.com/wp-content/uploads/2020/12/AutomationSubMenu.png)
*The Automation menu shows the submenu items on the category page.*

## Part 1: a method

Let's start with a method that generates the menu by calling `wp_nav_menu`. By default it generated the *entire* menu structure. But... we've added a `sub_menu` switch to the array. We'll use it to generate the sub menu.

```php
function ktt_get_sub_menu($menu = 'Main Menu', $menu_class = "tax-sub-menu")
{
    return wp_nav_menu(array(
        'menu'        => $menu,
        'sub_menu'    => true,
        'echo'        => true,
        'items_wrap'  => '<ul class="' . esc_attr($menu_class) . '" class="%2$s">%3$s</ul>',
        'walker'         => '',
    ));
}
```

The output is pretty plain: a bunch of `li` and `a` elements. This makes styling easier.

## Part 2: a hook

Let's hook into the `wp_nav_menu_objects` to filter the menu. We're only intereseted in the *children* of the current menu item. If the current page is not part of the menu, we'll return an empty array:

```php
add_filter('wp_nav_menu_objects', 'ktt_sub_menu_items', 10, 2);


// filter_hook function to react on sub_menu flag
function ktt_sub_menu_items($sorted_menu_items, $args)
{
    if (!isset($args->sub_menu)) {
        return $sorted_menu_items;
    }

    $current_id = 0;

    // find the current menu item
    foreach ($sorted_menu_items as $menu_item) {
        if ($menu_item->current) {
            $current_id = $menu_item->ID;
            break;
        }
    }

    if ($current_id == 0) return [];

    // only use items that are direct children of the current
    foreach ($sorted_menu_items as $key => $item) {
        if ($item->menu_item_parent != $current_id) {
            unset($sorted_menu_items[$key]);
        }
    }

    return $sorted_menu_items;
}
```

Note: I use the same page for both *tags* and *categories* and it works for both. The reason it works, is that we based it on the *menu* and the menu is agnostic to which objects are underneath it 😁.

## Usage

On my category page, I just echo the result after the description:

```php
<?php if ('' != get_the_archive_description()) : ?>
    <div class="archive-meta">
        <?php the_archive_description(); ?>
    </div>
<?php endif;
echo ktt_get_sub_menu();
```

## Shortcode?

But what if you wanted to use it in a widget or in a post (or only in certain places)? Well... you can turn it easily in a [shortcode](https://codex.wordpress.org/shortcode) and use it wherever you want:

```php
add_shortcode('highlights', 'ktt_sh_highlights');
function ktt_sh_sub_menu($atts)
{
    ob_start();
    ktt_get_sub_menu();
    $content = ob_get_contents();
    ob_end_clean();
    return $content;
}
```
