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.

/**
 * 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">&hellip;</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;
}

Bill Erickson

Bill Erickson is the co-founder and lead developer at CultivateWP, a WordPress agency focusing on high performance sites for web publishers.

About Me
Ready to upgrade your website?

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

Let's Talk

Reader Interactions

Comments are closed. Continue the conversation with me on Twitter: @billerickson

Comments

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

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

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

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