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, which defeats the purpose of using it.
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. You can view a demo of the code (and all combinations of options) on the demo page here: http://wp-sub-menu-demo.christianvarga.com/.
I originally wrote a function that generated the menu manually, however Thomas Dexter kindly pointed out a method that uses 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 modified it to work based purely on the menu structure (irrelevant of page structure). So all you need to do is drop this in your functions.php file.
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 (as opposed to my original function which only listed direct descendants and required convoluted code to fix).
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.