The menu builder in WordPress is great, however quite often I find myself needing to create a secondary menu on pages, containing just a subset of the menu. In my case the rules are simple: when viewing a top level page we want to list all child pages nested beneath it. When viewing a second level page, we want to list all sibling pages nested under the same parent. We could just use wp_list_pages and filter by a parent, but it won’t match the structure designed in the menu builder.

Surprisingly there’s little information or working solutions to this problem, so I’ve put together the following code which does exactly what we want.

To give you an idea of how it works with the default options, imagine you have a menu like this:

– A
– – B
– – C
– D

If you’re on page A, you’ll see B and C in the menu (A is a top level page, so it shows children). If you’re on B or C, again, you’ll see B and C (B and C are second level pages, so it shows siblings).

A big thanks goes out to Thomas Dexter who kindly pointed out a way to use wp_nav_menu, which adds the correct wp classes and allows us to use custom walkers and the like. Using his code and the hook provided by mac joost on Stack Overflow, I created a filter that works based purely on the menu structure (irrelevant of page structure). So all you need to do is drop this in your functions.php file (remember to remove the opening <?php tag first).

Then you can display it in your theme using wp_nav_menu (just like you normally would), but also passing in a sub_menu flag to activate the custom sub_menu function:

If you’re just using the default location as above, you’ll need to ensure that your custom menu is set as the primary navigation menu. You can do this in the Appearance > Menus section, under the Manage Locations tab. Otherwise, if you’ve registered a custom menu location, ensure that the menu is set properly in the Menus section, and point the theme_location parameter in your call to the name of the custom location.

Also, using the hook like this allows you to easily control the display of nested children using the depth parameter.

By default, if you have many nested levels, this code will filter the menu by the absolute top level parent. If you want the menu to drill down dynamically by filtering the menu based on the direct parent, pass a “direct_parent” => true flag to the wp_nav_menu call.

If you want to include the parent/root element in the menu, you can pass in a “show_parent” => true flag to display the root level item as well.