Building custom pagination links

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:

  1. 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 set end_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.
  2. 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.

<?php
/**
* Archive Navigation
*
* @author Bill Erickson
* @see https://www.billerickson.net/custom-pagination-links/
*
*/
function ea_archive_navigation() {
$settings = array(
'count' => 6,
'prev_text' => ea_icon( 'arrow-left' ),
'next_text' => ea_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']--;
// Previous
if( $current > 1 ) {
$settings['count']--;
$links[] = ea_archive_navigation_link( $current - 1, 'prev', $settings['prev_text'] );
}
// Current
$links[] = ea_archive_navigation_link( $current, 'current' );
// 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 ) {
$links[] = ea_archive_navigation_link( $current + 1, 'next', $settings['next_text'] );
}
echo '<nav class="navigation posts-navigation" role="navigation">';
echo '<h2 class="screen-reader-text">Posts navigation</h2>';
echo '<div class="nav-links">' . join( '', $links ) . '</div>';
echo '</nav>';
}
add_action( 'tha_content_while_after', 'ea_archive_navigation' );
/**
* Archive Navigation Link
*
* @author Bill Erickson
* @see 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;
$classes = array( 'page-numbers' );
if( !empty( $class ) )
$classes[] = $class;
$classes = array_map( 'sanitize_html_class', $classes );
$label = $label ? $label : $page;
$link = esc_url_raw( get_pagenum_link( $page ) );
return '<a class="' . join ( ' ', $classes ) . '" href="' . $link . '">' . $label . '</a>';
}
view raw functions.php hosted with ❤ by GitHub

Bill Erickson

Bill Erickson is a freelance WordPress developer and a contributing developer to the Genesis framework. For the past 14 years he has worked with attorneys, publishers, corporations, and non-profits, building custom websites tailored to their needs and goals.

Ready to upgrade your website?

I build custom WordPress websites that look great and are easy to manage.

Let's Talk

Reader Interactions

Comments

  1. 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.

    • 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.

Leave A Reply