I use the following code to change the title of WordPress posts and pages. But it changes nav menu item titles too, which I want to avoid.

I want to change the title of posts and pages in: home page, all archive pages and all widgets (recent posts widget, random post widget, etc.)

There are similar questions in both Stack Overflow and WP Stack Exchange suggesting to use in_the_loop() function. Unfortunately, it doesn't work for me, because if I place it, it also affects the sidebar widgets.

That means, if I use in_the_loop() function, the_title filter does not affect recent posts widget, random post widget etc.

So how can I apply the_title filter for just post and page titles, but not menu titles?

function pppp_title_update( $title, $id = null ) {

    if ( ! is_admin() ) {
       if(is_singular(array('post','page')) || is_archive() || is_home()){
                $current_post_id = get_the_ID();
                $new_titile = get_post_meta($current_post_id, 'pp_new_title',true);
                return $new_titile;  

    return $title;
add_filter( 'the_title', 'pppp_title_update', 10, 2 );
  • Have you tried e.g. if( ! is_nav_menu_item( $id ) ){ ... } within your filter callback to avoid filtering the title of nav menu items? – birgire Jul 23 '18 at 10:26
  • 1
    if( ! is_nav_menu_item( $id ) ) check will exclude a custom nav menu item link, however, if the nav menu item itself is a page or post, it'll be filtered twice with the_title filter. In that case, this condition will work only once (i.e. not when the_title is fired for filtering the original post / page title within the menu). – Fayaz Jul 24 '18 at 3:24

Problem Description:

Let me rephrase the question first. You want to:

  1. Set new title to all post and page type from a meta field.

  2. You want this to happen everywhere (home page, single page, widgets etc.)

  3. However, you don't want this title change to happen if the title is on the Navigation Menu.


Before I give you the CODE, let me explain a few points first (based on your CODE):

How to change titles of all posts and pages:

You already know the use of the_title filter. However, if you want to target all post and page type titles (but not custom post types), then your condition:

is_singular(array('post','page')) || is_archive() || is_home()

will not work. For example, it'll change custom post type on an archive page or the home page as well. This condition doesn't check if the title we are filtering is a page or post type. Instead, it checks if the page itself is either singular (post or page) or it's an archive (category, tag etc.) page or the home page. So custom post types in these pages also gets affected. Additionally, if there is a widget in a custom post type (singular) page, then by this logic, page or post titles in that widget will not be affected there.

To fix that, we need a different check, like:

$post = get_post( $id );
if ( $post instanceof WP_Post && ( $post->post_type == 'post' || $post->post_type == 'page' ) )

Why Navigation Menu title is also changed & how to stop it:

WordPress applies the_title filter twice on the navigation menu items' title (if the menu items correspond to existing posts or pages).

  1. First as the corresponding post or page title. This happens in the wp_setup_nav_menu_item() function of wp-includes/nav-menu.php file.

  2. Then as the Menu item title itself. This happens in the Walker_Nav_Menu class.

For your requirement, we need to stop the the_title filter both the times.

Fortunately, WordPress has two filters: pre_wp_nav_menu fires before filtering menu titles and wp_nav_menu_items fires after filtering menu titles. So we can use these two filters to first remove the the_title filter for nav menu item titles and then add the the_title filter back again for other titles.


You may use the following CODE in the theme's functions.php file or as a separate plugin:

function wpse309151_title_update( $title, $id = null ) {
    if ( ! is_admin() && ! is_null( $id ) ) {
        $post = get_post( $id );
        if ( $post instanceof WP_Post && ( $post->post_type == 'post' || $post->post_type == 'page' ) ) {
            $new_titile = get_post_meta( $id, 'pp_new_title', true );
            if( ! empty( $new_titile ) ) {
                return $new_titile;
    return $title;
add_filter( 'the_title', 'wpse309151_title_update', 10, 2 );

function wpse309151_remove_title_filter_nav_menu( $nav_menu, $args ) {
    // we are working with menu, so remove the title filter
    remove_filter( 'the_title', 'wpse309151_title_update', 10, 2 );
    return $nav_menu;
// this filter fires just before the nav menu item creation process
add_filter( 'pre_wp_nav_menu', 'wpse309151_remove_title_filter_nav_menu', 10, 2 );

function wpse309151_add_title_filter_non_menu( $items, $args ) {
    // we are done working with menu, so add the title filter back
    add_filter( 'the_title', 'wpse309151_title_update', 10, 2 );
    return $items;
// this filter fires after nav menu item creation is done
add_filter( 'wp_nav_menu_items', 'wpse309151_add_title_filter_non_menu', 10, 2 );
  • 1
    Thanks for the detail answer. Seems like this will be worked fine. Thanks again... But I am on a trip. I will check your answer on this Friday and let you know the result. – Ranuka Jul 25 '18 at 0:20
  • That's OK, take your time. – Fayaz Jul 25 '18 at 0:29
  • 1
    This is worked without any problem. Thanks for the answer and explanation :-) – Ranuka Jul 28 '18 at 5:53

WordPress navigation editor has ability to change menu title regardless of the title of the post/page.

If you need more automated solution code below will replace title of the post/page everywhere using the_title filter but restore default title of the menu item using nav_menu_item_title filter.

 * Replace post/page title on home, single and archive pages.
 * @param string $title   Post title
 * @param int    $post_id Post ID
 * @return string New post tilte
function wpse_309151_get_replace_default_title_from_meta( $title, $post_id ) {

    $post_type = get_post_type( $post_id );

    if( !is_admin() && ( $post_type === 'post' || $post_type === 'page' ) ) {

        $new_title = get_post_meta( $post_id, 'wpse_309151_post_title', true);

        if( $new_title && !empty( $new_title ) ) {
            return $new_title;

    return $title;

add_filter( 'the_title', 'wpse_309151_get_replace_default_title_from_meta', 10, 2 );

 * Restore default post/page title in navigation
 * @param string   $title The menu item's title.
 * @param WP_Post  $item  The current menu item.
 * @param stdClass $args  An object of wp_nav_menu() arguments.
 * @param int      $depth Depth of menu item. Used for padding.
 * @return string Restored post title
function wpse_309151_get_restore_default_title_for_navigation( $title, $item, $args, $depth ) {

    // Remove filter to not affect title
    remove_filter( 'the_title', 'wpse_309151_get_replace_default_title_from_meta', 10, 2 );

    $post_id = $item->object_id;
    $title   = get_the_title( $post_id );

    // Add the title filter back
    add_filter( 'the_title', 'wpse_309151_get_replace_default_title_from_meta', 10, 2 );

    return $title;

add_filter( 'nav_menu_item_title', 'wpse_309151_get_restore_default_title_for_navigation', 10, 4 );

If you have the post object (get_the_ID(); or $post->ID; are essentially the same) See https://stackoverflow.com/questions/22351038/get-the-current-page-id-inside-wordpress-plugin-page, then you should also be able to do use either https://developer.wordpress.org/reference/functions/get_post_type/ to check the type or

if (in_array($post->post_type, array('post', 'page')) {
... swop the title

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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