According to the docs, wp_get_nav_menu_items() parameter 1 accepts

(string) (Required) Menu name, ID, or slug.

Here is how I'm registering my menus

        'primary' => 'Navigation primaire',
        'secondary' => 'Navigation secondaire',

Here is how I have tried to load one

var_dump( wp_get_nav_menu_items('primary') );             // bool(false)
var_dump( wp_get_nav_menu_items('navigation-primaire') ); // bool(false)
var_dump( wp_get_nav_menu_items('Navigation primaire') ); // bool(false)

var_dump( wp_get_nav_menu_items(51) );                      // array([...]) (correct)

So, the function will only return the menu when I use the ID. I would much rather use the slug since my site is multilingual and I don't want to have to use different ID's depending on the language.

Am I doing something wrong or is this a core bug? I've tried this on the TwentySixteen theme with my multilingual plugin disabled (Polylang).

4 Answers 4


Since nobody has really explained why the function doesn't work the way you thought it did, I shall take a stab at explaining it in detail as I just fell for the same trap as you did.

This is where the docs aren't really clear as to what the slug is referring to. Most people would assume its the slug of the registered menu but thats not correct. Its actually the slug of the term for the taxonomy nav_menu.

Lets dive into the source code to see how the Wordpress core interprets the slug of the menu.

function wp_get_nav_menu_items( $menu, $args = array() ) {
    $menu = wp_get_nav_menu_object( $menu );

The first line shows that it uses a function named wp_get_nav_menu_object() to retrieve the menu. It passes the $menu as the first parameter to this function. Since this is what we use for our slug we will have to dive into the source for wp_get_nav_menu_object().

function wp_get_nav_menu_object( $menu ) {
    $menu_obj = false;

    if ( is_object( $menu ) ) {
            $menu_obj = $menu;

    if ( $menu && ! $menu_obj ) {
            $menu_obj = get_term( $menu, 'nav_menu' );

            if ( ! $menu_obj ) {
                    $menu_obj = get_term_by( 'slug', $menu, 'nav_menu' );

            if ( ! $menu_obj ) {
                    $menu_obj = get_term_by( 'name', $menu, 'nav_menu' );

we can see that this function uses the get_term_by function to retrieve the $menu_obj. This is where we get our menu.

When you create a menu in the Wordpress admin area, it creates a new term. Terms, have slugs based off of their name. So if you have a menu name of "My Awesome Menu" Wordpress will generate a slug named "my-awesome-menu". You may have registered the menu as "primary" but that is not the slug these functions refer to. Its actually the menus term slug.

So to be clear, lets just say you registered a menu like so:

register_nav_menu('primary', 'my primary nav');

primary is not the slug. my-primary-nav is also not the slug.

Since we created a menu named "My Awesome Menu" (as an example), and the slug is 'my-awesome-menu', you can retrieve it like so:

$awesome_menu = wp_get_nav_menu_items( 'my-awesome-menu' );

You can see for yourself what the slug is just by checking the database in the wp_terms (or whatever your prefix is) table and look for your menu name in there.

Since you are using a multilingual site or even a multisite, this method won't be the best way to go about it. Since the name and slug could possibly be different.

So the best method is to get the all the menu locations with get_nav_menu_locations() which will return an associative array where the key is the slug of the registered menu and the value is the id of the menu term selected for its location.

With the term id, we can get that terms info and then return the proper slug the function is requiring.

Below is a function that utilizes the wp_get_nav_items() function but you would pass in the registered menu slug. In your case this would be 'primary'.

Code Solution

// kind of a long function name but whatevs
function get_menu_items_by_registered_slug($menu_slug) {

    $menu_items = array();

    if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_slug ] ) ) {
        $menu = get_term( $locations[ $menu_slug ] );

        $menu_items = wp_get_nav_menu_items($menu->term_id);


    return $menu_items;

  • 1
    Excellent explanation.
    – Bagseye
    Nov 26, 2021 at 10:23
  • So well explained, unlike the docs (will try to add this in comments). Lost hours with this thinking it was the hook timing.. THANKS.
    – cdsaenz
    Nov 23, 2023 at 23:34

Small modification to the accepted answer

After removing a menu from menu location, $locations[ $menu_slug ] returns 0, so it's good to add this check:

function get_menu_items_by_registered_slug($menu_slug) {
  $menu_items = array();

  if ( ($locations = get_nav_menu_locations()) && isset($locations[$menu_slug]) && $locations[$menu_slug] != 0 ) {
    $menu = get_term( $locations[ $menu_slug ] );
    $menu_items = wp_get_nav_menu_items($menu->term_id);

  return $menu_items;

Try this:

    $menu_name = 'primary'; 

    if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_name ] ) ) {
        $menu = wp_get_nav_menu_object( $locations[ $menu_name ] );

        $menu_items = wp_get_nav_menu_items($menu->term_id);
        // Your output code
  • Could you explain a bit why your answer would work?
    – cjbj
    Aug 25, 2016 at 10:36
  • Doesn't seem to work for me. get_nav_menu_locations() returns an array with a single item ('primary' => 51), but wp_get_nav_menu_items() returns false when using a slug. Aug 25, 2016 at 14:54
  • Just realized I can use the array from get_nav_menu_locations() to load the correct menu per language without needing to define constants. Thanks for making me aware of the function. Aug 25, 2016 at 15:09

function custom_menu( $theme_location ) {
if ( ($theme_location) && ($locations = get_nav_menu_locations()) && isset($locations[$theme_location]) ) {
    $menu = get_term( $locations[$theme_location], 'nav_menu' );
    $menu_items = wp_get_nav_menu_items($menu->term_id);
    //your menu code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.