4

I want to hide an item from a menu if a user is logged out.

I am currently using the below code that achieves this using two separate menus, but to save duplication, I would like to only have to manage one nav menu.

function my_wp_nav_menu_args( $args = '' ) {

    if ( is_user_logged_in() ) { 
        $args['menu'] = 'logged-in';
    } else { 
        $args['menu'] = 'logged-out';
    }

    return $args;
}
add_filter( 'wp_nav_menu_args', 'my_wp_nav_menu_args' );

Is it possible to hide just one item for a logged out user, rather than doing it the way I currently am?

3
  • what do you want to hide from one menu ?
    – mmm
    Commented Jul 31, 2016 at 9:33
  • A link which not visible to not registered user. Commented Jul 31, 2016 at 10:02
  • The "without a plugin" thing is never necessary. Such logic is independent from your design and styling and should survive a redesign of your site. Put it inside a plugin or a mu-plugin. Having hundreds of mini-plugins instead of a big functions.php or an extremely large plugin does not slow down your site.
    – kaiser
    Commented Jul 31, 2016 at 12:51

7 Answers 7

-3

Find the class or id of the menu item that you want to hide. suppose the class of that menu is logged-in-menu

Then in header.php file of your theme before closing head tag use the below code

<style>
<?php if(! is_user_logged_in() ) : ?>
    .logged-in-menu{
        display: none;
    }
<?php endif; ?>
</style>
3
  • 1
    This means that you are (a) querying unnecessary data and (b) is non-reversible for child themes or plugins. You might want to at least use a callback attached to wp_head or properly register and enqueue styles. While then it would be a somehow ok solution, it would still be preferable to just conditionally switch those items off before querying the items from the DB.
    – kaiser
    Commented Jul 31, 2016 at 12:03
  • For anyone thinking of using this, please don't. Use the proper menu hooks as per @chrisguitarguy answer. Commented Mar 4, 2021 at 11:16
  • This is crap. Any script kiddie can then still see your menu.
    – user75665
    Commented Mar 9, 2023 at 0:22
6

As @chrisguitarguy already added a more than valid answered while I was writing this answer, here's a simple addition to the other two answers.

The return value of the wp_setup_nav_menu() function has a filter, which has $menu_item as only value provided – exactly before it is returned – and it is of type object and a \stdClass with the following public properties that you can check against:

  • ID: The term_id if the menu item represents a taxonomy term.
  • attr_title: The title attribute of the link element for this menu item.
  • classes: The array of class attribute values for the link element of this menu item.
  • db_id: The DB ID of this item as a nav_menu_item object, if it exists (0 if it doesn't exist).
  • description: The description of this menu item.
  • menu_item_parent: The DB ID of the nav_menu_item that is this item's menu parent, if any. 0 otherwise.
  • object: The type of object originally represented, such as "category," "post", or "attachment."
  • object_id: The DB ID of the original object this menu item represents, e.g. ID for posts and term_id for categories.
  • post_parent: The DB ID of the original object's parent object, if any (0 otherwise).
  • post_title: A "no title" label if menu item represents a post that lacks a title.
  • target: The target attribute of the link element for this menu item.
  • title: The title of this menu item.
  • type: The family of objects originally represented, such as "post_type" or "taxonomy."
  • type_label: The singular label used to describe this type of menu item.
  • url: The URL to which this menu item points.
  • xfn: The XFN relationship expressed in the link of this menu item.
  • _invalid: Whether the menu item represents an object that no longer exists.

So a simple callback will allow you to use some conditional logic and then maybe exclude an item:

add_filter( 'wp_setup_nav_menu', function( \stdClass $item ) {
    # Check conditionals, and invalidate an item in case
    $item->_invalid = is_user_logged_in() 
        && 'post' === $item->object
        && 'post_type' === $item->type
        # && … whatever you need to check for your invalidation of an item
    ;

    return $item;
} );

The exclusion logic lives inside the _invalid property and is executed by the _is_valid_nav_menu_item( $item ) function that is a callback used when nav menu items are retrieved. It uses it inside a array_filter() to reduce the number of items depending on this flag.


As extension to @MD Sultan Nasir Uddin solution: While a CSS only solution will work, goal should be to not even have the data in this request, in the database query and in the render pipeline. For a complete answer, here still is the how: Example using wp_add_inline_style() to inline the styles and PHP heredoc syntax for readability:

<?php
/** Plugin Name: Hide menu items for logged in users */

# Add class:
add_filter( 'wp_nav_menu_args', function( Array $args ) {
    if ( is_user_logged_in() )
        $args['menu_class'] .= '  logged-in';
    return $args;
} );

# Add inline styles
add_action( 'wp_enqueue_scripts', function() {

    $styles = <<<STYLES
.logged-in .special-item {
    display: none;
}
STYLES;

    wp_add_inline_style( 'custom-style', $styles );
} );

You could probably just use the body classes to find a logged-in or similar class for a more specific target as well – instead of adding an additional class like above.

2
  • That _invalid thing is nifty. Commented Jul 31, 2016 at 13:11
  • @chrisguitarguy It is! And I did not know about it unless you posted the answer and I read through source. Only thing I am unsure now is if it should not just be false when you want to drop something.
    – kaiser
    Commented Jul 31, 2016 at 14:41
6

Filter wp_nav_menu_objects. It will contain the sorted list of nav menu items to render. Have a look at wp_setup_nav_menu_item for some properties you can use.

Here's a quick (untested) example.

add_filter( 'wp_nav_menu_objects', function( array $items, object $args ) {

    if ( 'someThemeLocation' !== $args->theme_location ) {
        return $items;
    }

    return array_filter( $items, function( $item ) {
        return '/user-specific-thingy' === $item->url 
            && ! is_user_logged_in();
    } );

}, 10, 2 );
1
  • This code is invalid, because wp_nav_menu_objects passes array $sorted_menu_items, stdClass $args, not array $items, array $args
    – user75665
    Commented Mar 9, 2023 at 0:28
2

After that I make it done using css nth child the procedure is

add_action('wp_head','hide_menu');

function hide_menu() { 
    if ( is_user_logged_in() ) {
        //
    } else {
        $output="<style> .menu li:nth-child(3) { display: none; } </style>";
    }
    echo $output;
}

Thanks all of you for your effort :)

1
  • This won't work if the menu item moves position. Commented Mar 4, 2021 at 11:13
1

I don't like the idea of just hiding menu items with CSS as they would still be visible using the code inspector on the page. For anyone looking for a more secure way of removing menu items, this should do the trick:

In this example I want to remove a "Downloads" menu item for anyone that is not currently logged in.

add_filter( 'wp_nav_menu_objects', 'remove_menu_items' ), 10, 2 );
function remove_menu_items( $items, $args ) {

    // If you need to remove several menu items, then it's good practice to set a variable for the check beforehand so that it's not running the same check function repeatedly.
    $logged_in = is_user_logged_in();

    foreach ( $items as $item_index => $item ) {
        // Remove "Downloads" menu item when not logged in
        if ( $item->title == 'Downloads' ) {
            if ( !$logged_in ) {
                unset($items[$item_index]);
            }
        }
    }
    return $items;
}
1

Based on @jamio answer I agree that if you just display:none the menu its still accessible thru inspect element, therefore I come up with the solution that if you want to hide the nav menus based on their class you can use classes[0] instead of title so that you can now just add a class on menu instead on setting each menu thru code

 add_filter( 'wp_nav_menu_objects', 'remove_menu_items', 10, 2 );
function remove_menu_items( $items, $args ) {

    $logged_in = is_user_logged_in();

    foreach ( $items as $item_index => $item ) {
        // Remove menu item with "hide_when_logout" class when not logged in
        if ( $item->classes[0] == 'hide_when_logout' ) {
            if ( !$logged_in ) {
                unset($items[$item_index]);
            }
        }
    }
    return $items;
}
0
<?php
//Then in header.php file of your theme before closing head tag use the below code
//https://wordpress.stackexchange.com/questions/233667/how-to-hide-an-item-from-a-menu-to-logged-out-users-without-a-plugin
    <?php
 if ( is_user_logged_in() ) {
       $output="<style>.menu-item-8685 {display: none !important;}</style>";
echo $output;
    } else {  
         echo '<script>alert("Welcome vistor")</script>';
    }
     ?>
2
  • 1
    That's a combination of the accepted answer and Iqbal's answer?
    – Rup
    Commented Mar 4, 2021 at 0:26
  • yes this is a answer | and worked perfect with me ... Commented Mar 14, 2021 at 17:36

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.