Back to Community

Troubleshooting Custom Post Type Archive Pagination and Duplicate Posts

32 threads Sep 7, 2025 CoreAlpha/beta/rc

Content

If you've created a custom post type (CPT) archive page and then tried to modify the query to sort or change the number of posts, you may have encountered a frustrating issue: duplicate posts appearing on the first page or the archive functionality breaking entirely. This is a common problem that stems from how the main query is modified.

Why This Happens

When you use functions like query_posts() or create a new WP_Query on an archive template without carefully preserving the original query variables, you can inadvertently overwrite the main query. The original query already contains crucial information, such as the post type and pagination data. When you replace it with a new query that doesn't include this context, WordPress can no longer correctly identify that it should only be displaying posts from your specific CPT, leading to unexpected results like duplicate posts or all posts being displayed.

Common Solutions

1. Use the pre_get_posts Hook (Recommended)

The most robust method for modifying queries, especially on archive pages, is to use the pre_get_posts action hook within your theme's functions.php file. This method alters the query before it is run, avoiding the issues caused by overwriting it later.

function my_modify_cpt_archive_query( $query ) {
    // Check if on the front end and the main query for your CPT archive
    if ( ! is_admin() && $query->is_main_query() && is_post_type_archive( 'your_cpt_slug' ) ) {
        $query->set( 'orderby', 'title' );
        $query->set( 'order', 'ASC' );
        $query->set( 'posts_per_page', 30 ); // Change number of posts
    }
}
add_action( 'pre_get_posts', 'my_modify_cpt_archive_query' );

2. Properly Use query_posts() with $query_string

If you must modify the query directly in your template file (e.g., archive-your_cpt.php), ensure you append the original query string. This helps preserve the context of the query, such as the post type and pagination information.

// Append to the original query instead of replacing it
query_posts( $query_string . '&orderby=title&order=asc&posts_per_page=30' );

// Your loop here

wp_reset_query(); // Always reset when done

3. Explicitly Define the Post Type

When creating a custom query, always explicitly define the post type you are querying. This prevents the query from defaulting to the standard 'post' type.

// For a new WP_Query
$custom_query = new WP_Query( array(
    'post_type'      => 'your_cpt_slug', // This is critical
    'orderby'        => 'title',
    'order'          => 'ASC',
    'posts_per_page' => 30,
    'paged'          => get_query_var( 'paged' ) // Important for pagination
) );

// Your loop using $custom_query here

wp_reset_postdata(); // Reset post data after a custom query

Important Considerations

  • Avoid query_posts(): The query_posts() function is not recommended by many developers because it completely overrides the main query and can cause significant performance and compatibility issues. Using pre_get_posts or a secondary WP_Query is preferred.
  • Pagination: If you change posts_per_page, ensure you include 'paged' => get_query_var( 'paged' ) in your custom query arguments so pagination functions like next_posts_link() work correctly.
  • Reset Queries: Always use wp_reset_query() after query_posts() and wp_reset_postdata() after a custom WP_Query to restore the global $post data and main query.

By understanding how WordPress handles its main query and using the correct methods to modify it, you can successfully create sorted and paginated custom post type archives without encountering duplicate posts or other unexpected behavior.

Related Support Threads Support