Categories
Tutorials WordPress Tips

How to add proper pagination as default in WordPress themes

When you scroll to the bottom of an index page on a WordPress site, what do you see? Chances are it’s two links, one saying “Older Posts” and one saying “Newer Posts”. Scarcely has any default setting been more engagingly uninformative, and unnecessarily so. This “let’s not tell the visitor where she is on the site nor how much other content is available” approach to navigation is the epitome of User Experience archaism and should be but a footnote in the annals of WordPress history. Instead it is a cornerstone of the free WordPress theme experience and the default setting for all baseline WordPress themes (_s included) with one groundbreaking exception: Twenty Fourteen.

It is time we the people who build WordPress themes cast aside the rotting corpse of Older and Newer posts and replace it with proper pagination. After all the function is already built into WordPress – in fact it has been for a very, very long time. So, without further ado let me provide you with the code you need to get started and a few tweaks to move your WordPress pagination into the 21st century.

paginate_links() – the old friend you never knew you had

You don’t need fancy plugins or custom code to get proper pagination in your index pages. The paginate_links() function will do that for you in a snap. Here it is in its most basic form, straight from the Codex:


<?php
global $wp_query;

$big = 999999999; // need an unlikely integer

echo paginate_links( array(
	'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
	'format' => '?paged=%#%',
	'current' => max( 1, get_query_var('paged') ),
	'total' => $wp_query->max_num_pages
) );
?>

(If you’re wondering what the $big variable is for it helps output a clean URL when the page is loaded. Pay no mind.)

Slot this code into your template file where you want the pagination to appear (i.e. replace the current Older Posts and Newer Posts code with this one) and you’re good to go. If you want the basic solution that is. Which probably isn’t what you want.

Options Aplenty

Luckily for us we don’t have to be content with the default example above. paginate_links() has plenty of parameters we can use to change its output. The parameters you should care about from a design perspective are:

  • end_size: Decides how many numbers to display at the start and end of the list. Defaults to 1 meaning you’ll get output like this: 2 … 5 6 7 … 10. If you change the number to 2 you’ll get 2 3 … 5 6 7 … 9 10. You get the idea. Visually 1 looks best but in some cases you may want to change this value.
  • mid_size: Decides how many numbers to display on the left and right side of the current page (not including the current page). Defaults to 1. So if you set mid_size to 2 and are on page 6 you’d get 2 … 4 5 6 7 8 … 10.
  • prev_next: True or false decides whether a Previous and Next link should appear in the list or not. The actual wording is controlled separately (see below).
  • prev_text and next_text: The actual text output displayed if prev_next is set to true. Default output is « Previous and Next » and the strings are wrapped and prepared for translation.
  • type: Decides what the function outputs. The default plain outputs a list of anchor tags (links) which imo makes no sense. This can be switched to either array (great if you want complete control and know how to handle arrays in PHP) or list which outputs an unordered list and should be the default setting. I strongly recommend  setting type to list so you can work with the output in a reasonable way.

There are other variables but they are technical and control the functionality which I’ll address at the end of this article.

Upcycling existing code

Proper WordPress Pagination
So, let’s say you want to add pagination to your site (not unlike what you see directly above here) and you want to do it in a clean way. In that case it behooves you to create a function, stash it in functions.php or template-tags.php in your theme or child theme, and call it in when necessary. The easiest way to get this working, and working properly, is to upcycle the function provided in Twenty Fourteen and make some subtle changes to its output:


<?php

if ( ! function_exists( 'yourtheme_paging_nav' ) ) :
/**
 * Display navigation to next/previous set of posts when applicable.
 * Based on paging nav function from Twenty Fourteen
 */

function yourtheme_paging_nav() {
	// Don't print empty markup if there's only one page.
	if ( $GLOBALS['wp_query']->max_num_pages < 2 ) {
		return;
	}

	$paged        = get_query_var( 'paged' ) ? intval( get_query_var( 'paged' ) ) : 1;
	$pagenum_link = html_entity_decode( get_pagenum_link() );
	$query_args   = array();
	$url_parts    = explode( '?', $pagenum_link );

	if ( isset( $url_parts[1] ) ) {
		wp_parse_str( $url_parts[1], $query_args );
	}

	$pagenum_link = remove_query_arg( array_keys( $query_args ), $pagenum_link );
	$pagenum_link = trailingslashit( $pagenum_link ) . '%_%';

	$format  = $GLOBALS['wp_rewrite']->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : '';
	$format .= $GLOBALS['wp_rewrite']->using_permalinks() ? user_trailingslashit( 'page/%#%', 'paged' ) : '?paged=%#%';

	// Set up paginated links.
	$links = paginate_links( array(
		'base'     => $pagenum_link,
		'format'   => $format,
		'total'    => $GLOBALS['wp_query']->max_num_pages,
		'current'  => $paged,
		'mid_size' => 3,
		'add_args' => array_map( 'urlencode', $query_args ),
		'prev_text' => __( '&larr; Previous', 'yourtheme' ),
		'next_text' => __( 'Next &rarr;', 'yourtheme' ),
		'type'      => 'list',
	) );

	if ( $links ) :

	?>
	<nav class="navigation paging-navigation" role="navigation">
		<h1 class="screen-reader-text"><?php _e( 'Posts navigation', 'yourtheme' ); ?></h1>
			<?php echo $links; ?>
	</nav><!-- .navigation -->
	<?php
	endif;
}
endif;

?>

To make this work in a template simply call the function like this:


<?php yourtheme_paging_nav(); ?>

The function will output clean HTML markup with the pagination links wrapped in list items within an unordered list like this:


<nav class="navigation paging-navigation" role="navigation">
	<h1 class="screen-reader-text">Posts navigation</h1>
	<ul class='page-numbers'>
		<li><a class="prev page-numbers" href="http://localhost/page/7/">&larr; Previous</a></li>
		<li><a class='page-numbers' href='http://localhost/'>1</a></li>
		<li><span class="page-numbers dots">&hellip;</span></li>
		<li><a class='page-numbers' href='http://localhost/page/5/'>5</a></li>
		<li><a class='page-numbers' href='http://localhost/page/6/'>6</a></li>
		<li><a class='page-numbers' href='http://localhost/page/7/'>7</a></li>
		<li><span class='page-numbers current'>8</span></li>
		<li><a class='page-numbers' href='http://localhost/page/9/'>9</a></li>
		<li><a class='page-numbers' href='http://localhost/page/10/'>10</a></li>
		<li><a class='page-numbers' href='http://localhost/page/11/'>11</a></li>
		<li><span class="page-numbers dots">&hellip;</span></li>
		<li><a class='page-numbers' href='http://localhost/page/14/'>14</a></li>
		<li><a class="next page-numbers" href="http://localhost/page/9/">Next &rarr;</a></li>
	</ul>
</nav><!-- .navigation -->

Now all you need is some basic CSS to make it all look good:


/* =Index pagination
----------------------------------------------- */

/* The containing box with a nice white background */
.paging-navigation {
	font-family: sans-serif;
	padding: 1em;
	background: #fff;
	background: hsl(0, 0%, 100%);
}

/* Remove bullets and list indentation */
.paging-navigation ul {
	list-style-type: none;
	margin: 0;
	padding: 0;
}

/* Make the list items appear horizontally */
.paging-navigation li {
	display: inline;
}

/* Give each link and the current item some padding to make them easy to click */
a.page-numbers,
span.page-numbers {
	padding: .3em .7em;
	color: #333;
	color: hsl(0, 0%, 20%);
}

/* Link hover state */
a:hover.page-numbers {
	color: #000;
	color: hsl(0, 0%, 0%);
}

/* Current page bold and dark */
.paging-navigation .current {
	font-weight: bold;
	color: #000;
	color: hsl(0, 0%, 0%);
}

Of course you can elaborate on this to your heart’s content, but this is the gist of it.

Go Forth and Paginate

Now that you see how easy it is to add proper pagination to your WordPress index pages I think you will agree with me there is no good reason to keep the old Older and Newer posts links in place. If you don’t agree with me, by all means leave your disagreements in the comments below and let’s have a discussion. Otherwise, familiarize yourself with paginate_links(); and give your visitors a better user experience.