Better WordPress pagination

UPDATED 6/6/09: Added query string to work with search pagination.
Ever wanted to have better pagination controls in WordPress? Those “Newer posts / Older posts” links are a bit limited. Much better is the ubiquitous numbered list, a la Google’s results page.
There is at least one plugin for this. As ever, I’m keen to keep plugin overhead to a minimum, and where possible to just include a simple function in my custom themes for basic functionality like this. Here’s the function:
function pagination( $query, $baseURL ) {
$page = $query->query_vars["paged"];
if ( !$page ) $page = 1;
$qs = $_SERVER["QUERY_STRING"] ? "?".$_SERVER["QUERY_STRING"] : "";
// Only necessary if there's more posts than posts-per-page
if ( $query->found_posts > $query->query_vars["posts_per_page"] ) {
echo '<ul class="paging">';
// Previous link?
if ( $page > 1 ) {
echo '<li class="previous"><a href="'.$baseURL.'page/'.($page-1).'/'.$qs.'">« previous</a></li>';
}
// Loop through pages
for ( $i=1; $i <= $query->max_num_pages; $i++ ) {
// Current page or linked page?
if ( $i == $page ) {
echo '<li class="active">'.$i.'</li>';
} else {
echo '<li><a href="'.$baseURL.'page/'.$i.'/'.$qs.'">'.$i.'</a></li>';
}
}
// Next link?
if ( $page < $query->max_num_pages ) {
echo '<li><a href="'.$baseURL.'page/'.($page+1).'/'.$qs.'">next »</a></li>';
}
echo '</ul>';
}
}
A little explanation is in order…
The function has been written with “custom loops” as well as the standard WordPress Loop in mind. It can be used when you define your own loop with the WP_Query object, as well as on normal templates that use the have_posts() construct. So, for custom loops built using the instructions in the last linked article, pass $recentPosts (or whatever you call your custom loop query object) as the first parameter. On normal loop templates, pass $wp_query. In either case, this query object contains some very useful info (like max_num_pages) that really help keep the above code simple.
I’ll leave the second parameter up to you to construct. It just has to be the basic URL of the listing page, onto which pagination stuff will be appended. It should include the trailing slash.
I’ve also included the query string in the right place, which should make this function work for pagination on WordPress search results. One oddity in this respect is that when you have something other than the front page of your site as your main post listing page (on this site it’s /blog/), the paging URLs don’t seem to work. So /blog/page/2/ just returns a 404. I’ve fudged around this by setting the $baseURL parameter of the call to this function on the blog to just / (the root). It kind of works. Anyone know why including the page slug doesn’t work?
Well, the rest should be self-explanatory. Again, as with many of my WordPress tips here, this is aimed at theme developers who know their way around WP and PHP a bit. If you’re not quite there yet, maybe try the plugin I linked to at the start of this post…
Oh, as an added bonus, the CSS I use for the pagination list!
ul.paging { list-style: none; padding: 0; font-weight: bold; }
ul.paging li { float: left; margin: 0 6px 6px 0; }
ul.paging li a, ul.paging li.active { display: block; padding: 3px 6px; background-color: #f5f5f5; border: 1px solid #ccc; }
ul.paging li.active { background-color: #000; border-color: #000; color: #fff; }
Welcome! I build websites - mostly based on the brilliant, free & open 
Patternhead (9th July 2009)
This looks really useful.
Thanks for sharing :)
WP Tim (11th July 2009)
Please post the where this function is called or how to incorporate in a page. Thanks!
Steve Taylor (11th July 2009)
Tim, you would just call the function where you want the pagination controls. The basic call (say, after The Loop in
index.php) would be:pagination( $wp_query, "/" );If often include one before The Loop too, but set to only appear if not on the first page:
if ( $paged ) slt_pagination( $wp_query, "/" );For
archive.php, you’ll probably have to set up the$baseURLparameter dynamically, depending on the type of archive being shown. (Hint: the WP functionsget_category_link()andget_month_link()help out a lot!)If you’re not following, then it’s possible that a plugin might be your cup of tea. For the most part I’m addressing theme developers here.
Good luck!
sean (24th August 2009)
Hello, I’m trying to improve my search.php page with your code. No more than 5 posts should be displayed per page.
Here’s the code before yours was added:
<a href="" title=""></a>Brand: <!-- End of Byline Containing Div -->
<a href="" title="">more...</a>
Here’s how I’ve added yours:
query_vars["paged"];
if ( !$page ) $page = 1;
$qs = $_SERVER["QUERY_STRING"] ? "?".$_SERVER["QUERY_STRING"] : "";
// Only necessary if there's more posts than posts-per-page
if ( $query->found_posts > $query->query_vars["posts_per_page"] ) {
echo '';
// Previous link?
if ( $page > 1 ) {
echo '<a href="'.$baseURL.'page/'.($page-1).'/'.$qs.'" rel="nofollow">« previous</a>';
}
// Loop through pages
for ( $i=1; $i max_num_pages; $i++ ) {
// Current page or linked page?
if ( $i == $page ) {
echo ''.$i.'';
} else {
echo '<a href="'.$baseURL.'page/'.$i.'/'.$qs.'" rel="nofollow">'.$i.'</a>';
}
}
// Next link?
if ( $page max_num_pages ) {
echo '<a href="'.$baseURL.'page/'.($page+1).'/'.$qs.'" rel="nofollow">next »</a>';
}
echo '';
}
} ?>
<a href="" title=""></a>
Brand: <!-- End of Byline Containing Div -->
<a href="" title="">more...</a>
No Results Found
Please try your search again using different keywords.
This is my first attempt at pagination, but I’ve used Wordpress for 5-6 projects now.
Steve Taylor (24th August 2009)
Hi Sean, from the code you posted it’s not really clear what your issue is. Could you be more specific as to what the issue is? (Please don’t post more code unless necessary, it needed a lot of tidying up!)
If you don’t understand the code enough to explain what the problem is, you might be better off with a plugin – check out the link at the top of this post.
Valid Information (11th November 2009)
Looks complicated but very useful information
ayman (9th January 2010)
how can i limit number of pages that displayed ?not all pages
Steve Taylor (9th January 2010)
Hi Ayman, you mean the number of total pages? Or to abbreviate the pagination numbers when there’s a lot, like this?
« previous 1 2 3 4 ... 23 24 25 next »Abbreviating the pagination numbers is certainly something I’ll try and get round to one day – should be pretty easy. Maybe one of the current pagination plugins does that?
Regarding limiting the total number of pages, these pages should help:
http://weblogtoolscollection.com/archives/2008/04/13/define-your-own-wordpress-loop-using-wp_query/
http://digwp.com/2009/12/limit-posts-without-plugin/