WordPress includes the paginate_links() function for building paginated navigation links on archive pages. It works pretty well, but there are two things I’d like to change:
- The
end_size
parameter lets you specify how many of the first and last pages to show (default is 1). But there’s no way to setend_size = 0
. When you’re a few pages deep into an archive, you’ll always have “1” at the start and the last page at the end, with dots separating it from the middle links. - The
mid_size
parameter specifies the number of links to the left and right of the current page. But I’d like to treat them separately, and have more pages after the current page visible. You can’t make your pagination start with the current page and only show next pages.
While these may seem like minor changes, they would help reduce the varying length of the navigation links.
Here’s what paginate_links()
generates on the first page of a category archive:
And here’s what it looks like on the 5th page:
It takes up too much space and distracts your users from what they care about – the current page, previous link, and next link.
It’s also difficult to style a navigation area that varies that much. It would look like this on mobile:
A better way
I prefer showing the previous/next arrows, current page, and some number of next pages. Here’s what the navigation looks like now:
Every archive page shows the same number of links. This ensures a consistent look and user experience as they browse your archives. You can choose a number of links that fit on a single line on mobile.
Use the $settings
array at the top to modify the total number of links shown and the previous/next link text.
/**
* Archive Navigation
*
* @author Bill Erickson
* @link https://www.billerickson.net/custom-pagination-links/
*
*/
function ea_archive_navigation() {
$settings = array(
'count' => 7,
'prev_text' => be_icon( [ 'icon' => 'arrow-left' ] ),
'next_text' => be_icon( [ 'icon' => 'arrow-right' ] ),
);
global $wp_query;
$current = max( 1, get_query_var( 'paged' ) );
$total = $wp_query->max_num_pages;
$links = array();
// Offset for next link
if( $current < $total )
$settings['count']--;
if( $current + 3 < $total ) {
$settings['count'] = $settings['count'] - 2;
}
// Previous
if( $current > 1 ) {
$settings['count']--;
$links[] = ea_archive_navigation_link( $current - 1, 'pagination-previous', $settings['prev_text'] );
$settings['count']--;
$links[] = ea_archive_navigation_link( $current - 1 );
}
// Current
$links[] = ea_archive_navigation_link( $current, 'active' );
// Next Pages
for( $i = 1; $i < $settings['count']; $i++ ) {
$page = $current + $i;
if( $page <= $total ) {
$links[] = ea_archive_navigation_link( $page );
}
}
// Next
if( $current < $total ) {
if( $current + 3 < $total ) {
$links[] = '<li class="pagination-omission">…</li>';
$links[] = ea_archive_navigation_link( $total );
}
$links[] = ea_archive_navigation_link( $current + 1, 'pagination-next', $settings['next_text'] );
}
echo '<nav class="archive-pagination pagination" role="navigation">';
echo '<h2 class="screen-reader-text">Posts navigation</h2>';
echo '<ul>' . join( '', $links ) . '</ul>';
echo '</nav>';
}
add_action( 'tha_content_while_after', 'ea_archive_navigation' );
/**
* Archive Navigation Link
*
* @author Bill Erickson
* @link https://www.billerickson.net/custom-pagination-links/
*
* @param int $page
* @param string $class
* @param string $label
* @return string $link
*/
function ea_archive_navigation_link( $page = false, $class = '', $label = '' ) {
if( ! $page )
return;
$label = $label ? $label : $page;
$link = esc_url_raw( get_pagenum_link( $page ) );
$output = '';
if ( ! empty( $class ) ) {
$output .= '<li class="' . esc_attr( $class ) . '">';
} else {
$output .= '<li>';
}
$output .= '<a href="' . $link . '">' . $label . '</a></li>';
return $output;
}
Anh Tran says
Nice solution. I’d love to have the current page is always at the middle.
PS: it would be easier to read if the code has correct identation.
Bill Erickson says
Thanks for pointing out the indentation issue. It was actually a site-wide issue due to “Minify HTML” stripping out the spacing. I didn’t see it because logged in users didn’t get the minified version. All fixed now!
If you want to maintain the same number of pages to the left and right, use paginate_links(). That was one of the things I purposely changed. Alternatively, you could modify the code above to divide the
$settings['count']
in two and add links before and after the current page.Kosta says
I want to create a completely custom pagination structure on the home.php template – what would you recommend? Everything I have used so far is not quite right – I’m converting to WP from HTML so everything needs to be exactly as it was on the previous site (for example the anchor tags have to be within span tags).
I think you can get an array back – what code would you recommend?
Bill Erickson says
The example shown above should work for you. The
paginate_links()
function lets you specify exactly the markup you want used for the links.Thanh says
Thanks. Nice solution. But I can’t know how to use this function.
Rumi says
What if I want to use above code with Custom Post Query or WP_Query
Bill Erickson says
Pagination in WordPress only works with the main query, so first you’ll need to overwrite the
global $wp_query
with your custom query. See my article on Pagination in a Custom Query for more information.Once you have completed that, both the standard WordPress pagination links and the custom ones shown in this article will work.
Craig says
How would I make it always show the back arrow even on page 1?
Bill Erickson says
Right below
// Previous
you’ll see the back arrow is addedif( $current > 1 )
. Add anelse
statement to the end of that to load a non-functional back link when on the first page.Example: https://gist.github.com/billerickson/ce5c10c4921ae004869ccc5735e55e52#file-functions-php-L32-L36
I recommend you also add some CSS so the link with a
.disabled
class is visibly different, like.page-numbers.disabled { opacity: .5; }
Mohammad Zainabal T. Jauddin says
The current page number is stuck on the first page number
is there a way to make it move from the start to end?
just to make the pagination more interactive and fluid