If you have been working with WordPress for a while, no doubt you have been stumped on how to handle custom post type pagination.

“WordPress offers built-in functionality for navigating through posts. Theme developers can use simple links or numbered pagination to indicate the previous page or the next page in a given sequence.”

Codex

The problem is, this pagination built into WordPress does not work on custom post types, it only works using standard posts.

I’m going to be the first to admit that I was really stuck on this one. I went through various tutorials online including paginate links, wpza and even the wpsmith article to build a plugin in composer using custom rewrite URLs. None of them worked.

After much trial and error, I am going to share the code with you for how I got CPT pagination running successfully on my project.

  1. Go to your functions file and using the template redirect hook, and unhook redirect_canonical.

2. Next we are going to declare our pagination function and use the paged parameter within our wp_query loop. We are also going to remove our redirect canonical and template redirect filters. The gist is below if you’d like to copy:

1. // use the page url and append the CPT template file you would like to redirect canoncial URL's
add_action( 'template_redirect', 'insight_template_redirect', 1 );
function insight_template_redirect() {
if ( is_paged() && is_single( 'yourwebsite.com/category' ) ) {
remove_action( 'template_redirect', 'redirect_canonical' );
}
}
2. // Declare pagination function and use paged parameter in wp_query
function pagination($prev = '«', $next = '»') {
global $wp_query, $wp_rewrite;
$wp_query->query_vars['paged'] >2 ? $current = $wp_query->query_vars['paged'] : $current = 1;
$pagination = array(
'base' => @add_query_arg('paged','%#%'),
'format' => '',
'total' => $wp_query->max_num_pages,
'current' => $current,
'prev_text' => __($prev),
'next_text' => __($next),
'paged' => $current,
'type' => 'plain'
);
if( $wp_rewrite->using_permalinks() )
$pagination['base'] = user_trailingslashit( trailingslashit( remove_query_arg( 's', get_pagenum_link( 1 ) ) ) . 'page/%#%/', 'paged' );
remove_filter('template_redirect', 'redirect_canonical');
if( !empty($wp_query->query_vars['s']) )
$pagination['add_args'] = array( 's' => get_query_var( 's' ) );
echo paginate_links( $pagination );
};
view raw functions.php hosted with ❤ by GitHub

3. Next, we need to alter the custom query in our custom post type archive page. For practical purposes I have called this template archive-template but if your custom post type category is moves you would call this archive-movies.php.

4. after your loop/endif statement, call in the pagination function using a div class of pagination

// for practical purposes I have called this template archive-template
but if your custom post type category is moves you would call this archive-movies.php.
I have set the posts to 9, but you can set it to however many you want.
$paged = ( get_query_var('paged')) ? get_query_var('paged') : 1;
$args = array(
'post_type' => 'insight',
'post_status' => 'publish',
'posts_per_page' => 9,
'paged' => $paged,
'order' => 'DESC',
);
// after your loop/endif statement, call in the pagination function using a div class of pagination
<div class="pagination"><?php pagination('»', '«'); ?></div>

5. Finally, we need to jazz it up with some CSS. Feel free to change the hex colours and styles as required

// go ahead and change the colours according to your brand
.pagination {
clear:both;
padding:20px 0;
position:relative;
font-size:11px;
line-height:13px;
}
.pagination span, .pagination a {
display:block;
float:left;
margin: 2px 2px 2px 0;
padding:6px 9px 5px 9px;
text-decoration:none;
width:auto;
color:#fff;
background: #4d4d4f;
}
.pagination a:hover{
color:#fff;
background: #0097D6;
}
.pagination .current{
padding:6px 9px 5px 9px;
background: #0097D6;
color:#fff;
}
view raw style.css hosted with ❤ by GitHub

And that is is my friends! Now when you click on page 2, instead of showing the URL of /page/2 and refreshing the first page, your custom post type template will calculate how many pages you have based on your max posts per page, and paginate through them.

I really hope this saves someone some time, and they don’t get stuck on this as I did.

Alpha Omega Digital is a WordPress agency based in Melbourne, Australia but also services clients from Sydney, Brisbane, Newcastle, Perth, Adelaide, Darwin and Hobart. Have a project in mind? Contact me here.

Similar Posts