WordPress Custom Post Type Pagination

Looking around the internet it’s easy to find several examples of how you can create WordPress custom post type pagination links using multiple different methods. The problem is many of them require overwriting or modifying the $wp_query global variable, which can break functionality with plugins, themes, or theme widgets. It’s almost always better practice to create an entirely new WP_Query instance and store it in your own custom variable.

Another added complication is that many methods also assume that the custom post type has the archive enabled, but what if it’s not? There are situations where you may want to create an entirely custom post archive using a page template and assigning that page template to a page. This allows the archive to be viewed using any path you desire (by setting it in the post editor) and more importantly it allows the archive page to be added to a WordPress menu using the normal page selector. However, page numbers are appended as sub nodes in the url, such as /news/page/2. Depending on your site map WordPress may not properly resolve the url and report that the page cannot be found. In most cases this is due to the custom archive page conflicting with the custom post type rewrite slug value. An example of this problem would be the custom archive page url being http://laubsterboy.com/news and the custom post type rewrite slug being ‘news’. In this example when the archive page 2 is viewed WordPress assumes you’re trying to view a news custom post type with a url / title of /news/page/2. Obviously this is not what you’re wanting. To fix this either the archive page url needs to be changed (possibly to something like /all-news), or the custom post type rewrite slug needs to be changed (possibly to something like ‘news-item’).

With the problem outlined lets look at some code.

Here is an example of creating a custom WP_Query object to display the custom post type archive.

  $paged = (get_query_var('paged')) ? get_query_var('paged') : 1; 
  $news = new WP_Query(array('post_type' => 'news', 'posts_per_page' => get_option('posts_per_page'), 'paged' => $paged));  

  while ($news->have_posts()) : $news->the_post(); 

  <!-- Post Template Here-->

<?php endwhile;


The above code assumes that you’re using, or modifying, the WordPress Twenty Fourteen Theme. Next, we’ll need to modify the inc/template-tags.php file to allow the twentyfourteen_paging_nav function to accept the max_num_pages parameter.

if ( ! function_exists( 'cla_base_paging_nav' ) ) :
* Display navigation to next/previous set of posts when applicable.
function twentyfourteen_paging_nav($max_num_pages = 0) {
	// Don't print empty markup if there's only one page.
	if ( $GLOBALS['wp_query']->max_num_pages < 2 && $max_num_pages < 2 ) {

	$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'    => $max_num_pages !== 0 ? $max_num_pages : $GLOBALS['wp_query']->max_num_pages,
		'current'  => $paged,
		'mid_size' => 1,
		'add_args' => array_map( 'urlencode', $query_args ),
		'prev_text' => '&larr; Previous',
		'next_text' => 'Next &rarr;',
	) );

	if ( $links ) :

	<nav class="navigation paging-navigation" role="navigation">
		<h1 class="screen-reader-text">Posts navigation</h1>
		<div class="pagination loop-pagination">
			<?php echo $links; ?>
		</div><!-- .pagination -->
	</nav><!-- .navigation -->

The above code simply looks for the max_num_pages parameter and if it’s set then it will override the wp_query max_num_pages value.

At this point you should have a functioning custom post type archive page, with the custom post type has_archive set to false, that uses the WordPress reading settings to set how many posts to show per page.

I hope this has been helpful and let me know if you have any questions or suggestions for improvement.

Leave a comment

Your email address will not be published. Required fields are marked *

You can also subscribe without commenting.