Theme Development

Custom Post Type Pagination Chaining Method


Custom post type pagination got you down? There's been nothing more frustrating in developing for WordPress than getting custom post type pagination to work. I've developed a method that's solved my woes and I think it'll solve yours too.


As I've started to create more premium WordPress themes I've begun developing a base theme as a sort of framework to build on with every new project. The process was going good until I started working with custom post types. That's when I inevitably went up against my long time foe, custom post type pagination.

Since the release of custom post types in WordPress 2.9 their pagination has proven difficult depending on the circumstance. Even heavy-hitting WordPress professionals have been stumped from time to time.

Thankfully, I feel I've finally beat custom post type pagination once and for all. I imagine myself flexing over it in the glow of the moonlight, my face chiseled in accomplishment, one foot planted on the ground and the other firmly on its chest.

My key to easy custom post type pagination is using the archive-posttype.php template to do something I call chaining, meaning that we use them as includes in other WordPress template files where pagination is needed. What this does is cut down on custom development for different circumstances in which a developer would want pagination. Think of it as using the custom post type archive template as an index or catch-all. There's a few twists along the way, but that's the big idea. Let's begin.

Step 1 Pagination

Since coding is a foundation science lets start with the issue of pagination itself. Even though I bow to the greatness of the pagination plugin, WP PageNavi, as Jacob Goldman recently pointed out, there's really no need for it. WordPress has its own pagination function called paginate_links() and apparently most developers know nothing about it. Be sure to read its documentation, but to save you the time of fashioning your own code for functions.php here's mine similar to the Codex example:

function paginate() {
	global $wp_query, $wp_rewrite;
	$wp_query->query_vars['paged'] > 1 ? $current = $wp_query->query_vars['paged'] : $current = 1;
	$pagination = array(
		'base' => @add_query_arg('page','%#%'),
		'format' => '',
		'total' => $wp_query->max_num_pages,
		'current' => $current,
		'show_all' => true,
		'type' => 'plain'
	if ( $wp_rewrite->using_permalinks() ) $pagination['base'] = user_trailingslashit( trailingslashit( remove_query_arg( 's', get_pagenum_link( 1 ) ) ) . 'page/%#%/', 'paged' );
	if ( !empty($wp_query->query_vars['s']) ) $pagination['add_args'] = array( 's' => get_query_var( 's' ) );
	echo paginate_links( $pagination );

My chaining method has been developed with this function in mind. Using WP PageNavi for custom post type pagination gets real ugly so I won't be showing you how to do that for the same reason friends don't let friends drive drunk. You're welcome. But let's move on to what you really came for - finally figuring out how to keep custom post type pagination from throwing a 404 or always reverting back to page one.

Step 2 Custom Post Type Archive Template

Since custom post types don't have their own index pages, and like I said before, I think of the archive-posttype.php template as its stand-in specifically because I use it as the foundation for pagination in all other templates. A lot of developers will first stress a Page Template as an index page, but 1.) I obviously disagree and 2.) we'll get to those later. Even WordPress superstar, Justin Tadlock, agrees custom post types should have at least the option of their own index pages.

Thankfully, my paginate() function out of the box with the archive-posttype.php template. Phew. But the problem is its pagination is bound by the setting for posts per page in Settings > Reading. And because custom post types are just that, custom, nine times out of ten a developer will want their posts per page to be custom as well. To do this the easiest way I've come by is writing a filter in functions.php like this:

function portfolio_posts_per_page( $query ) {
    if ( $query->query_vars['post_type'] == 'portfolio' ) $query->query_vars['posts_per_page'] = 1;
    return $query;
if ( !is_admin() ) add_filter( 'pre_get_posts', 'portfolio_posts_per_page' );

This method came to me by way of Jonathan Christopher's post called WordPress Posts Per Page Per Custom Post Type. Thank's, Jonathan!

What happens if I don't want my permalink structure to have the same name of my custom post type (e.g. I'm glad you asked. This is one of the reasons a developer would prefer a Page Template to display their custom post types. It gives the user full control over the permalink structure because they can simply change the name of the Page that's using that Page Template. That's understandable and we'll do that soon, but for those of us who don't need to do that or want another way in the future there's a small edit we can make under the hood to bend that structure to our will.

The function for creating a custom post type, register_post_type(), accepts an argument called rewrite. That argument's value is passed as an array and changing the value of the slug will change the permalink structure. Here's an example:

'rewrite' => array( 'slug' => 'insertyourpermalinknamehere', 'with_front' => true ),

After you've changed this go to Settings > Permalinks and hit the "Save Changes" button to dump your rewrite cache. Visit your site and refresh the page to view the change. Done and done. Again though, the only disadvantage of this method is non-tech-savvy users won't be able to change the name of their permalink structure from the admin GUI unless of course they're headed for the Theme Editor to change that rewrite slug.

Step 3 Page Templates

It's not uncommon for developers to display and paginate their custom post types in a Static Front Page by way of a Page Template. The reason for this is two-fold; it gives the user that permalink structure name-changing ability we just talked about and it enables them to display custom post types on the front page of their site. But things can get ugly here. I'm ashamed to say I once coded custom post type pagination three different ways in one theme. This is partly due to using WP PageNavi, but I own this embarrassing fact to stress that this entire process can be overwhelming for theme developers who aren't in "the know".

Let's call our Page Template page-portfolio.php to match the naming convention for custom post type templates even though it's not one. Here's the code:

/* Template Name: Portfolio */

$paged = 1;
if ( get_query_var('paged') ) $paged = get_query_var('paged');
if ( get_query_var('page') ) $paged = get_query_var('page');

query_posts( '&post_type=portfolio&paged=' . $paged );

require_once( 'archive-portfolio.php' );

This is the first time you're seeing the archive-posttype.php template chained to another template. And it works! But what the heck is going on? This janky $paged variable business highlights the exact problem developers seem to be having with custom post type pagination. Basically if this fix (cough) isn't in place and a user clicks to view page 2, like someone who's been bopped over the head, WordPress gets confused and doesn't know where it's at. And to add insult to injury apparently WordPress knows this and accepts it as normal development procedure by publishing this note in the Pagination Parameters section of the Codex page for WP_Query():

Pagination Note: You should set get_query_var( 'page' ); if you want your query to work with pagination. Since WordPress 3.0.2, you do get_query_var( 'page' ) instead of get_query_var( 'paged' ). The pagination parameter 'paged' for WP_Query() remains the same.

It makes sense to me that developers should be able to set that variable and point to a specific page. What doesn't make sense is why pagination inherently works with default post types (i.e. post, page, attachment), but not with custom ones.

Beyond having to patch the code there is one other catch here in that you can't call your Page slug the same thing as your custom post type slug. Think of your custom post type slug as a reserved keyword; however, you can make the title of your Page the same name as your custom post type slug just as long as your Page slug is something different.

Step 4 Front Page Template

Using the front-page.php template locks users into a custom front page without the ability to change it (unless they delete the file or rename it temporarily). That's why most developers opt for the Page Template method of creating Static Front Pages, but for the sake of my method let's say we're using the former. To achieve custom post type pagination for this template all we need to do is simply include what we've done for archive-posttype.php like this:

require_once( 'page-portfolio.php' );

Step 5 Custom Post Type Taxonomies

The taxonomy-posttype-taxonomy.php template works in much the same way as the front-page.php template, but instead of including the page-portfolio.php template, you'll instead include our index, archive-posttype.php, like this:

require_once( 'archive-posttype.php' );

That completes the extent of my method. Where I was once banging my head against the desk at two in the morning I'm now calmly moving through projects at two in the afternoon. Victory! Well, at least for now. I'm not so naive as to think I may not find myself in a situation where this method doesn't work for custom post type pagination, but I haven't yet.


Unlike my other posts I hope this one becomes outdated. My hope is that WordPress will act on the recent survey that shows 92% of all developers use WordPress as a CMS and take a second look at how pagination works across their platform. The team at WordPress is nothing less than professional. I imagine a future where there's built in pagination tools and possibly a custom post type admin page under Settings for administering posts per page per custom post type. But for now I hope this chaining method helps the many developers I've seen suffering on the WordPress Forums. Please feel free to download, use and abuse the following zipped files as a framework for successful custom post type pagination. Enjoy!

Custom Post Type Pagination Chaining Method Framework (ZIP)

Related Posts
  • Code
    Creative Coding
    Advanced Use of Attachments in WordPress: Creating Custom Queries for AttachmentsAdvanced use of attachments in wordpress 400
    This tutorial is the second in a four part series in which you'll learn some techniques for working with images in attachments in WordPress which give you advanced options.Read More…
  • Code
    Quick Tip: Post Types, Taxonomies and PermalinksPost types taxonomies urls
    Custom Post Types and taxonomies are two powerful features of WordPress. Unfortunately, they can have a tendency to cause problems if developers aren't familiar with how permalinks, URLs, and rewriting works within WordPress. In this quick tip, we aim to cover the topic very briefly to make sure you know all you need to know about WordPress URLs, custom post types, taxonomies, and how they all relate.Read More…
  • Code
    Theme Development
    Creating a WordPress Theme from Static HTML: Creating an Archive TemplateCreating wordpress theme from html 400
    If you've been working your way through this series, you now have a functioning theme with two page templates. The steps I've demonstrated to this point are: preparing your markup for WordPress converting your HTML to PHP and splitting your file into template files editing the stylesheet and uploading your theme to WordPress adding a loop to your index file adding meta tags, the wp_head hook and the site title and description to your header file adding a navigation menu adding widget areas to the header and sidebar adding widget areas, a colophon and the wp_footer hook to the footer file creating template files for static pages. Read More…
  • Code
    Theme Development
    Creating a WordPress Theme from Static HTML - Creating a Page TemplateCreating wordpress theme from html 400
    So far in this series, I've shown you how to create a fully functioning WordPress theme from static HTML. We've covered the following steps: preparing your markup for WordPress converting your HTML to PHP and splitting your file into template files editing the stylesheet and uploading your theme to WordPress adding a loop to your index file adding meta tags, the wp_head hook and the site title and description to your header file adding a navigation menu adding widget areas to the header and sidebar adding widget areas, a colophon and the wp_footer hook to the footer file. At the moment, your theme only has one template file for displaying content—the index.php file. A powerful feature of WordPress is the ability to use template files for different kinds of content.Read More…
  • Code
    Creative Coding
    Using WordPress for Web Application Development: Features: Custom Queries with WP_QueryApplication foundation 400
    We've been looking at how WordPress can be used as a foundation for application development, but one of the things that we've yet to cover that most modern frameworks offer is how to query the database to retrieve results for any given view. Specifically, we haven't talked about how to get information out of the database and insert it into our pages.Read More…
  • Code
    Create a Shortcode to List Posts With Multiple ParametersPost listing shortcode main image400
    On many of the client sites I build, I find there are times when I need to include a post listing on a page. I'm not talking about archive pages here, but adding a custom listing to an existing static page. For example, I might want to list some posts on the 'About' page, or the site may require an in-depth page for a topic, with a list of posts and custom post types related to that topic. One way to do this is by creating a custom page template to include the current page content plus the results of a second custom query, but if you want more flexibility over how you list posts, or just want to do it a few times, a shortcode will be a simpler solution. In this tutorial, I'll show you how to create a simple shortcode to list all posts of a custom post type, and then I'll expand on that to create a shortcode with various parameters that users can specify to list posts however they want.Read More…