I know that it's easy to add something to the end of a navigation menu using code like the below:

function add_last_nav_item($items) {
  return $items .= '<li><a href="#myModal" role="button" data-toggle="modal">Contact</a></li>';

However, I need to do something that appears to be more complex.

I have 4 main menus each with child menu items. I need to add an entry to the bottom of the 3rd menu.

Is this possible in PHP having received a menu via wp_nav_menu() ?

2 Answers 2


If you have control over the the wp_nav_menu() call, you can implement a custom Walker_Nav_Menu class for fine-grained control over the rendering of an array of nav menu items to HTML.

If the markup of the items you wish to add is the same as that which the default Walker_Nav_menu already produces, you can use a 'wp_get_nav_menu_items' filter hook in order to add "virtual nav menu item" objects to the array before they are processed into HTML:

 * Add a login/logout link to the nav menu in the `primary` location, as a child
 * of a menu item for a Page with the slug `members` (if present).
 * @see https://wordpress.stackexchange.com/a/390893/25324
 * @param WP_Post[] $items The WP_Post objects representing menu items in this menu.
 * @param WP_Term   $menu The WP_Term object representing this menu.
 * @param array     $args The arguments used to retrieve menu items associated with this menu.
 * @return array The filtered $items array.
function wpse390890_insert_primary_nav_login_link( $items, $menu, $args ) {
  if( is_admin() )
    return $items;

  // Bail out if this menu isn't `primary` or `primary` is being loaded in the dashboard.
  if( $menu->slug !== 'primary' || is_admin() )
    return $items;
  // Find the ID of the Page with the slug `members` in the menu items.
  foreach( $items as $item ) {
    if( $item->post_type !== 'page' || $item->post_name !== 'members' )
    $members_page_id = $item->ID;

  // Bail if the `members` page is not in this menu.
  if( ! isset( $members_page_id ) )
    return $items;
  $logged_in  = is_user_logged_in();
  $link_title = __( $logged_in ? 'Log In' : 'Log Out', 'wpse390890' );
  // Insert a new WP_Post object for the link.
  $items[] = new WP_Post(
    (object) [
      'ID'               => 'user-login',
      'menu_item_parent' => $members_page_id,
      'classes'          => [ 'member-auth-link' ],
      'type'             => 'virtual',
      'object'           => 'virtual',
      'title'            => $link_title,
      'attr_title'       => $link_title,
      'url'              => $logged_in ? wp_logout_url() : wp_login_url(),
  return $items;

add_filter( 'wp_get_nav_menu_items', 'wpse390890_insert_primary_nav_login_link', 10, 3 );

See wp_setup_nav_menu_item()'s implementation for hints on which fields to specify, and how WP_Post objects are converted into objects representing nav menu items. var_dump()ing $items in the filter above is also a good way to explore the format and properties of the items which are already in the menu.

  • Thanks for this @bosco!
    – AdamJones
    Commented Jun 25, 2021 at 16:29

You can do like this :

add_filter( 'wp_nav_menu_items', 'your_function', 10, 2 );
function your_function( $items, $args ) {
global $wp; 
if ($args->menu->term_id == {term_id}) :
    // convert string menu to array
    $new_items = preg_split('/<\/li>/', $items);
    // create array to stock results
    $final_items = array();
    // loop to search where you insert your item
    foreach ($new_items as $new_item) :
        $final_items[] = $new_item;
        // find your item
        if(strpos($new_item, '{menu_title}') !== FALSE) :
            // insert your item
            $final_items[] = '<li class="menu-item"><a href="{permalink}">{title}</a></li>';
    // convert to string before return results
    $items = implode('', $final_items);
return $items;


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.