WP archive templates are a crucial part of how WordPress works, and a lot of plugins rely on these templates working correctly.

What is a WP archive?

An “archive page” in WordPress refers to a collection of posts grouped by post type, category, tag, term, author, or date. The blog page (the post type archive for posts) and the search results page are also considered archive pages. The WooCommerce shop page is also an archive: it is a custom post-type archive of the post type “product”. The same is true for product category pages: they are custom taxonomy term archive pages.

WordPress automatically creates the queries on its archive templates. It has a built-in Template Hierarchy that defines which default archives are available and how you can add your own. When you create new custom post types or custom taxonomies (for example with plugins like Pods or Custom Post Type UI), these also will automatically have their own post-type and term archives.

For further explanation on how to determine if you are using an archive, read this.

Use a custom WP_Query or modify the WP archive?

What is the right approach if you want to use a WP archive query but customize how it works, for example, sort it in a different way or modify the number of posts per page?

We often see developers doing this by using a custom WP_Query in a WP archive template. The downside of this approach is that the database is getting queried twice, adding unnecessary overhead. It also breaks plugins that rely on WP’s default archive query.

If your desired query is very complicated, it may indeed be best to build it with a custom WP_Query. But make sure to place it on a single page or post template, not on a WP archive. If you don’t want or cannot move the custom WP_Query from the WP archive, read the instructions on how to make this work with FacetWP.

However, instead of using a separate custom WP_Query, it is often much easier to modify the already existing WP archive query with a pre_get_posts filter. Especially for basic query arguments like the post types to be fetched, the number of posts per page, or the sorting method.

How to use pre_get_posts to modify the WP archive query

With WP’s pre_get_posts hook, it is easy to modify existing WP archive queries. The hook fires after the query variable object is created, but before the actual query is run, making it possible to change any existing query argument, or inject new ones.

In this first example, we want to show 20 posts per page for all category archive pages:

add_action( 'pre_get_posts', function( $query ) {
  if ( $query->is_category() ) {
    $query->set( 'posts_per_page', 20 );
  }
});

In the following example, we’ll modify the blog listing (called “home” in WP) to include both posts and products, and order by title in ascending order (a-z):

add_action( 'pre_get_posts', function( $query ) {
  if ( $query->is_home() && $query->is_main_query() ) {
    $query->set( 'post_type', [
      'post',
      'product'
    ] );
    $query->set( 'orderby', 'title' );
    $query->set( 'order', 'ASC' );
  }

The following example adds a secondary, fallback sorting method by overriding the original orderby query argument, on category archive pages only:

add_action( 'pre_get_posts', function( $query ) {
  if ( $query->is_category() ) {
    $query->set( 'orderby', [
      'date'  => 'DESC', // Primary sort: by post date
      'title' => 'ASC'   // Secondary, fallback sort: by post title
    ] );
  }
} );

See WordPress’ WP_Query documentation for all query arguments you can change or define with $query->set().

The argument keys always need to be placed between single quotes. For the values, make sure to check the documentation. Some arguments expect strings (between single quotes, like orderby), some require numbers (without quotes, like posts_per_page). And others expect arrays (multiple strings or numbers between square brackets, like the post_type).

See also