Improving WordPress page navigation: fixing next_post_link() and previous_post_link()

Improving WordPress Page Navigation

WordPress’ next_post_link() and previous_post_link() functions are used to navigate between posts in single post (permalink) view. They work great…until you come to the very first or the very last post.

When viewing the latest post, next_post_link() displays nothing; when viewing the earliest post, previous_post_link() displays nothing. This can result in some less than friendly navigation.

In this post, I’ll show you how to use a built-in WordPress function and PHP conditionals to improve your WordPress page navigation.

The problem

Here is an example of next_post_link() and previous_post_link() at work on our blog’s old theme:

wp_post_functions_example

When viewing a post that is neither the newest nor the oldest, the post navigation links display as expected.

The trouble with these functions comes when you get to the first or last post. In the case of the first, you’ll have something like this:

wp_post_functions_missing_previous

When viewing the oldest post, the "previous post" link disappears, leaving an ugly hole.

And when viewing the last post, you’ll have something like this:

wp_post_functions_missing_next

Similarly, when viewing the newest post, the "next post" link disappears.

To the rescue: get_adjacent_post

We’re going to use the obscure WordPress function get_adjacent_post() to check if… (shockingly) …there is an adjacent post. If there isn’t, we output whatever HTML markup we like to take the place of the gaping hole we’re trying to fill. If there is an adjacent post, we output it as normal.

The function retrieves a link to the adjacent post (either next or previous) and takes the following form…

get_adjacent_post([bool $in_same_cat, string $excluded_categories, bool $previous])

…and has these parameters:

  • $in_same_cat : Whether the link should be in the same category
  • $excluded_categories : A comma-separated list of excluded category IDs
  • $previous : Whether to retrieve the previous post

The solution, with sample markup

The code block below contains the markup you should add to your single.php template file. Place it where you’d like your navigation to appear, then adjust the parameters and output to suit your needs.

<span id="prev">
   <?php previous_post_link('%link', '&laquo; Previous'); ?>
   <?php if(!get_adjacent_post(false, '', true)) { echo '<span>&laquo;Previous</span>'; } // if there are no older articles ?>
</span>
<span id="next">
   <?php next_post_link('%link', 'Next &raquo;'); ?>
   <?php if(!get_adjacent_post(false, '', false)) { echo '<span>Next &raquo;</span>'; } // if there are no newer articles ?>
</span>

I then added this line to my CSS file:

#prev span, #next span { color:#ccc; } /* lighter than the normal anchor text */

And here is the final result:

wp_post_functions_result

The final result, showing user-friendly language at both ends.

In the example above, I grayed out the inactive links. If you have more room, you might get creative by replacing the text with something like “Sorry, there aren’t any newer posts!”

This article was written by Ryan.

Ryan is the lead web developer at 3 Roads Media. He has been coding websites since 1996.

52 comments

  • JimmyBean

    October 1, 2009

    I don’t know If I said it already but …Excellent site, keep up the good work. I read a lot of blogs on a daily basis and for the most part, people lack substance but, I just wanted to make a quick comment to say I’m glad I found your blog. Thanks, :)

    A definite great read..Jim Bean

    Reply
  • chibi

    October 19, 2009

    Hi, thanks for this tutorial. I was wondering if you could tell me how to change the navigation system of a wordpress blog? Right now, on my home page- there’s a link which says “Previous Comic”, which leads to “/page/2″. Is it possible to make that link to a permalink of the previous post (comic), instead of page 2?

    I’ve been browsing the net and can’t seem to find out how to do this..

    Thanks for your time!

    Reply
    • Ryan Burney

      October 19, 2009

      Hi Chibi,

      The WordPress tags used in this tutorial only work on single post (permalink) pages, i.e. those pages that only show one post at a time.

      As such, there’s no way to do what you’re asking from the homepage. WordPress has a couple of functions that let you skip to older posts and newer posts; you can read about those here and here.

      Reply
      • Ryan Burney

        October 19, 2009

        Chibi,

        After doing a bit of searching I found a solution that should do the trick. Place the following bit of code in The Loop where you want the link to appear:

        $wp_query->is_single = true;
        previous_post_link();

        This will tell WP to treat the current page as a single page, even if it’s your home page. I hope that helps.

        Reply
        • chibi

          October 20, 2009

          Thanks so much for taking the time to reply :)

          I tried putting that code in and nothing seems to happen. I’m probably putting it in the wrong place.. it just seems to display the text.

          Can you be any more specific?

          Thanks!

          Reply
        • Ryan Burney

          October 20, 2009

          No problem ;)

          I tested this in my index.php file and it works for me:

          < ?php
             query_posts('showposts=1');
          
             if(have_posts()) : while(have_posts()) : the_post();
          
                $wp_query->is_single = true;
                previous_post_link();
          
                // display your most recent post here
          
             endwhile; endif;
          ?>

          The query_posts bit will show only your most recent post on the homepage, which is what I assume you’d like to do. Then you step into The Loop, and the next bit of code tells WP to treat your index.php like a single post page, so the previous_post_link function works like you want it to. Place this wherever you’d like the link to your previous comic to appear.

          Below that, you can use WP functions like the_title and the_content to display your most recent post however you like.

          Then we exit The Loop.

          Let me know if you have trouble and I can take a closer look at what you’re doing.

          Reply
          • chibi

            October 20, 2009

            Great! It works!
            Thank you so much for helping me out with this. You’re a real life saver!

          • Ryan Burney

            October 20, 2009

            Great – glad I could help!

    • Ryan Burney

      November 30, 2009

      Hi Fatihturan,

      It looks like there are just a couple of small differences between the code I provided above and the code in your custom page template. I’m looking at lines 44-47 of your template.

      You’re using the functions previous_posts_link and next_posts_link (with an “s”), but you should be using previous_post_link and next_post_link (without the “s”).

      Also, on line 47 of your template, make sure it reads like this:

      < ?php if(!get_adjacent_post(false, '', false)) { echo '< a href="javascript:;" rel="nofollow">Sonraki »'; } ?>
      

      You have (false, ”, true) instead of (false, ”, false).

      Reply
      • fatihturan

        November 30, 2009

        But if i use previous_post_link (without s) it’s doesn’t showing next/prev links.

        I think previous_post_link (without s) functions is using on single.php template.

        If you look my template you will see what i’m using (custom page template).

        Reply
        • Ryan Burney

          December 4, 2009

          Ahhh, I hadn’t noticed that you were using a custom template.

          The reason the prev/next links are disabled is because there are no other posts to show; you’ve limited your query to 2 posts, so WordPress is only going to pull those 2 posts and show them; there are no other posts to link to.

          Why not use single.php? You’ll have to reinvent the wheel if all you’re trying to do is get a custom template to function like single post view.

          If you have other posts that need to use single.php, and you also want to use single view to show some photos, then you can add some conditionals at the top of your single.php to check what page you’re on, e.g.:

          < ?php if(is_page('photos')) : ?>
             // do your thing
          < ?php else : ?>
             // standard single post view
          < ?php endif; ?>
          
          Reply
  • JP

    December 11, 2009

    I was going to use the empty() function to check if next_post_link and previous_post_link returned anything, then act accordingly, but your use of get_adjacent_post is more elegant. Thanks for sharing.

    –JP

    Reply
  • Missingtoof

    January 30, 2010

    Thanks! That get_adjacent_post function is exactly what I was looking for. Never heard of it before, but it did the trick. Put it in some nested if statements and got the empty nav spans conditionally removed from all my pages.

    Not sure how to post code or I’d share.

    Reply
  • Si Davies

    February 6, 2010

    Excellent article, I’ve been looking for a solution for this for too long! Thanks for posting.

    Reply
  • venkat

    April 6, 2010

    Hi Ryan,

    I’ve been looking for this solution.. and thanks for making it look easier. Can it be possible to add an hover effect to those next/previous links.. so, they reveal the post title when hovered??

    thanks again/

    Reply
    • Ryan

      April 7, 2010

      Hey Venkat,

      I’m glad you enjoyed the tutorial! If I understand you correctly, you’d like the links to say “Previous” and “Next” normally, but on hover display the title of the previous or next post. If that’s the case, you may be able to do this with straight CSS and some tweaks to your WordPress template.

      Try this:
      1. In your WP template, store the title of the blog post in a PHP variable (for example, $title = get_the_title('...');)
      2. Echo the title of your previous/next post into a < div> or other container, and hide it with some CSS
      3. On hover of the “Previous/Next” links, use CSS to hide the “Previous/Next” and then show the post titles.

      Reply
      • venkat

        April 7, 2010

        Man you are awesome.. thanks a load!

        Reply
  • venkat

    April 8, 2010

    Hi ryan.. i’ve tried to use get_the_title() variable… but.. couldn’t figure it well..

    Mind a code snippet when possible.. please!

    Reply
    • Ryan

      April 8, 2010

      Hey Venkat,

      Turns out you don’t need to use get_the_title(). Sorry :)

      Replace the code from the tutorial with this (be sure to remove the spaces!):

      < span id="prev">
         < div>< ?php previous_post_link('%link'); ?>
         < div>< ?php previous_post_link('%link', __('« Previous', 'elegante')); ?>
         < ?php if(!get_adjacent_post(false, '', true)) { _e('« Previous', 'elegante'); } ?>
      < /span>
      < span id="next">
         < div>< ?php next_post_link('%link'); ?>
         < div>< ?php next_post_link('%link', __('Next »', 'elegante')); ?>
         < ?php if(!get_adjacent_post(false, '', false)) { _e('Next »', 'elegante'); } ?>
      < /span>
      

      Then, in your theme’s CSS file, add the following rules:

      #prev div, #next div { display:none; }
      #prev:hover div, #next:hover div { display:inline; }
      #prev div + div, #next div + div { display:inline; }
      #prev:hover div + div, #next:hover div + div { display:none; }
      

      I’ve only tested in Firefox, so let me know if you have any trouble!

      Reply
      • venkat

        April 9, 2010

        thats cool ryan.

        Thanks for ya help. It works :)

        Reply
  • Jez Thompson

    June 18, 2010

    Hello.

    Where and how do i add the $in_same_cat into your simple example?

    Cannot seem to get it to work..

    Cheers :)

    Reply
    • Ryan

      June 18, 2010

      Hey there Jez,

      Take a look above, below the heading that says “The solution, with sample markup.” The get_adjacent_posts() function takes the $in_same_cat as the first parameter. Set it to “true” if you only want to retrieve posts that are in the same category as the one you’re viewing; “false” if you want to retrieve posts in any category.

      What exactly is not working for you? Maybe I can be of more help if you provide some more details.

      Reply
      • Jez Thompson

        June 18, 2010

        Cheers Ryan, so it looks like this…

        <?php if(!get_adjacent_post(true, '', true)) { echo '«Previous'; } // if there are no older articles ?>
        
        <?php if(!get_adjacent_post(true, '', false)) { echo 'Next »'; } // if there are no newer articles ?>
        

        Because i tried that and it just scrolls through all the posts, i am using this in a news category and want it to work just with say cat id 9..

        Reply
        • Ryan

          July 6, 2010

          Hey Jez,

          I was having trouble with email notifications so I apologize for the delayed response. If you take a look at the functions above you’ll see that they take a comma separated list of excluded categories as the second parameter. So to exclude categories 4 and 6 you would put:

          < ?php if(!get_adjacent_post(false, '4,6', true)) { echo '«Previous'; } ?>
          Reply
  • 2k

    July 6, 2010

    Hi there, thanx for great solution. But can you explain how to add in { echo ‘Next »’; } instead of word: Next.?

    Reply
    • Ryan

      July 6, 2010

      Hi 2K, I’m not sure what your question is; would you mind clarifying?

      Reply
      • 2k

        July 30, 2010

        Ryan, when using image like this, instead of text, is it possible to get alt=”previous post title name” instead alt=”previous”?

        Reply
        • Ryan

          August 2, 2010

          Hey 2k,

          I’m not really sure — you might be able to use get_the_title(), but I don’t know which post ID you’d pass in.

          Reply
  • Matt

    July 29, 2010

    How would you make the “previous” or “next” be an image button instead of text?

    Reply
    • Ryan

      July 29, 2010

      Hey Matt,

      All you need to do is replace this piece:

      echo '< span>«Previous< /span>';

      with something like:

      echo '< img src="xyz.png" alt="Previous" />';

      You can even replace the img HTML code with a variable:

      $image = '< img src="xyz.png" alt="Previous" />';
      
      echo $image;
      
      Reply
  • tim

    August 18, 2010

    thanks for the tip. just what is was looking for :)

    Reply
  • DNicholls

    January 6, 2011

    I have a wordpress blog that is fed into my website. I want only 1 post per page. Can I use the next and previous buttons to do this? I took your first code and put it in and it works great but it will take me to the outside wordpress blog and not keep it in my website template. Help!!

    Thanks!

    Reply
    • Ryan Burney

      January 10, 2011

      Hi DNicholls,

      I don’t fully understand your setup. Can you provide a link to your site so I can have a look?

      Reply
  • BJ

    January 21, 2011

    Wow thanks,

    I’ve been searching for this for ever.. I hate the WP-navi plugin with all the listed page numbers.

    Also the option to navigate foward and backward in the same category is so important!

    Thanks..

    Reply
    • Ryan Burney

      January 21, 2011

      BJ,

      Great! I always love to hear when these tutorials help people out.

      Reply
  • BJ

    January 29, 2011

    Hi Ryan,

    Is this also going to work on the category-page (template) ?
    I found the same above and bottom navigation inside the loop.

    And i also wish to show off both (NEXT and PREV) buttons when there is a page 2.

    This was the code i placed on my sidebar to navigate between posts on the same category. (works 100%)

    <?php previous_post_link('%link', 'Vorige', true, ''); ?><?php if(!get_adjacent_post(false, '' ,true)) { echo 'Vorige'; } // if there are no older articles ?>
    <?php next_post_link('%link', 'Volgende', true, ''); ?><?php if(!get_adjacent_post(true, '' ,false)) { echo 'Volgende'; } // if there are no newer articles ?>
    

    But now i want to do the same ‘echo thing’ on the category-page with this basic ‘Twentyten’ code. Only when there is more than one page to navigate else the navigation will not show up entirely i’m fine with that setup..

    <?php next_posts_link( __( '← Older posts', 'twentyten' ) ); ?>
    <?php previous_posts_link( __( 'Newer posts →', 'twentyten' ) ); ?>
    

    I try to put the ‘echo’ line behind it but that doesn’t work..

    Reply
  • Meghan

    May 15, 2011

    I am hoping you can tell me how to use this method or a similar method to have the links always show in my custom template.

    They are totally screwy right now and not working correctly.

    <?php previous_posts_link(''); ?>
    <?php if(!get_adjacent_post(false, '', false)) { echo ''; } // if there are no older articles ?>
    <?php next_posts_link('
    <?php if(!get_adjacent_post(false, '', false)) { echo ''; } // if there are no newer articles ?>
    Reply
    • Ryan

      May 23, 2011

      Hi Meghan,

      Sorry for taking so long to get back to you — I was on vacation last week ;)

      If you look closely at your code, you’ll notice that the next_posts_link() function is missing some things. What you have:

      < ?php next_posts_link('

      Should be:

      < ?php next_post_link('%link', 'Next »'); ?>

      That should do it -- let me know if you need anything else!

      Reply
  • Jefferson

    July 25, 2011

    I’m using the category parameter, so it just comes up blank when there are no more posts in the category. Thanks to your surfacing the “obscure” function, I can now link back to the home page. So… (in Suffusion theme):

    <td class='previous'>
      <?php previous_post_link('%link', '%title', TRUE) ?>
      <?php if(!get_adjacent_post(TRUE, '', TRUE)) { echo '<a href="/">Home</a>'; } ?>
    </td>
    <td class='next'>
      <?php next_post_link('%link', '%title', TRUE) ?>
      <?php if(!get_adjacent_post(TRUE, '', FALSE)) { echo '<a href="/">Home</a>'; } ?>
    </td>

    Thanks!

    Reply
    • Ryan

      July 26, 2011

      Awesome!

      I’m glad it worked for you! Also, it’s good to know these functions are still alive and well in WP 3.x.

      Reply
  • Jessica

    August 19, 2011

    Hi–great tutorial! Thanks. I’m just having trouble getting my categories excluded… thanks again, Jessica.

    Reply
  • Jessica

    August 19, 2011

    Sorry, meant to specify too that I’ve been building my own theme, & wanted to ask if I could have left a function out or something that would make category exclusion work. Cheers!

    Reply
    • Ryan

      August 22, 2011

      Hi Jessica,

      Which version of WordPress are you running? Would you also mind sharing the code you’re using? You can use the < pre > tag (without spaces) to wrap your code so it formats nicely.

      Reply
  • Rob

    January 11, 2012

    Hello,

    Is there a way to use this same feature but apply it to the standard next_posts_link and previous_posts_link to navigate backwards and forwards between pages that display several posts, without having either Previous or Next disappear on the very first and very last pages?

    I hope my explanation makes sense.
    Thanks so much for your help and time.

    Reply
    • Ryan

      January 11, 2012

      Hey Rob,

      Your question makes perfect sense; I’m just not sure if there is a function that does what you want. Have you tried asking in the WP forums? Here are three possible solutions:

      1. Try getting in touch with Mark Jaquith, lead dev for WordPress. His contact info is here.

      2. Try the get_next_post() function. I have no idea if it will work in your case, but I stumbled across it while looking for an answer.

      3. Try the Next/Previous Post Link Plus plugin. It may or may not address your specific question, but it looks like it might offer an alternative for you.

      Sorry I don’t have a definitive answer — hopefully one of those options helps!

      Reply
      • Rob

        January 11, 2012

        Hi Ryan,

        Thanks so much for your suggestions.

        Its interesting that an available method to solve this is so difficult to find.

        I have yet to post this on the WP forums – I’ll try that along with writing to Mark Jaquith.

        I did think of a solution, although I am having difficult working out how to do it exactly and I wanted to ask your advice.

        Basically my navigation is an include file that consists of ‘Previous’ (next_posts_link) and ‘Next’ (previous_posts_link) that I would like to be shown at all times.

        So I was thinking that for the homepage of my website that displays the latest posts in a typical blog fashion, I could include a different navigation (only on the homepage) that would display ‘Previous’ (next_posts_link) and ‘Next’ (with no link attached – just text) so that the ‘Next’ link doesn’t disappear.

        How would I do this? Since at the moment my index.php file is acting as the template for all of the pages with blog entries.

        Please let me know what you think would be the most logical way to do this.

        Thanks so much. I really appreciate your help.

        Rob

        Reply
    • Ryan

      January 12, 2012

      Ahh, I wasn’t aware of those two functions. Those should work beautifully. Thank you for sharing, and I’m glad you got things to work!

      And in reference to your previous question on putting something just on the homepage, you can use the is_front_page() or is_home() conditionals.

      Reply

1 pingback

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Notify me of follow-up comments via e-mail (you can also subscribe without commenting).

Denver web design by 3 Roads Media All materials © 2012