pre_get_posts
Allows changing a WP_Query query before it runs.
This action can modify the $wp_query object. The object is passed to the hook by reference, so any changes made to $query inside the callback affect the original WP_Query object. The callback does not need to return anything.
IMPORTANT! This action fires for every WP_Query query:
- the main query
- secondary queries
- admin panel queries
- widget queries
- and so on
Make sure you modify only the intended query. Before changing it, use the relevant conditional tags to precisely restrict the change (see the examples).
Functions commonly used inside this hook:
- $query->is_main_query() — modify only the main WordPress query.
- is_admin() — target or exclude admin panel queries.
- get_queried_object() — obtain data for the current query (current page).
Before using get_queried_object() inside this hook, check $query->is_author:
add_action( 'pre_get_posts', 'function_name' );
function function_name( $query ){
if( $query->is_author )
$qo = $query->queried_object();
else
$qo = get_queried_object();
}
This appears to be a bug: on an author page, get_queried_object() is called too early, before the query data is processed. Processing sets $wp_query->query_vars['author'], which get_queried_object() uses to set the user object on an author page. Without that data, the current user object cannot be obtained.
A ticket was created for this bug: https://core.trac.wordpress.org/ticket/51829
Usage
add_action( 'pre_get_posts', 'wp_kama_pre_get_posts_action' );
/**
* Function for `pre_get_posts` action-hook.
*
* @param WP_Query $query The WP_Query instance (passed by reference).
*
* @return void
*/
function wp_kama_pre_get_posts_action( $query ){
// action...
}
- $query(WP_Query)
- The WP_Query object.
Notes
The argument is passed by reference
The $query object is passed by reference, so there is no need to declare a global variable. Any changes to $query inside the callback immediately affect the original object.
Static page queries
pre_get_posts should not be used to change queries associated with static pages because is_page, is_singular, pagename, and other rewrite-dependent properties have already been set on the object. To change the query, use new WP_Query in the static page template instead.
Identifying the required query
When using pre_get_posts, determine exactly which query you are changing. The useful $query->is_main_query() method ensures that changes affect only main queries. Combine it with conditional tags to target only the intended pages.
For example, if you want to change the category page query but do not check is_category(), the changes will also affect queries in the admin panel, other site pages, and elsewhere. Always precisely identify the query being changed through the pre_get_posts action hook.
Use in the admin panel
This action can also change admin panel queries. In that case, make sure the changes target the posts listing page. For example, checking $query->is_main_query() and is_post_type_archive('movie') to modify the front-end query for movie posts will also change the query on edit.php?post_type=movie. Add a ! is_admin() check to prevent this.
Warning: conditional tags
This action fires before the WP_Query object is fully initialized, so some conditional tags based on WP_Query data do not work yet. For example, is_front_page() does not work, while is_home() does. It is therefore better to work directly with object data, such as $query->is_search.
Complete list of properties that can be used instead of conditional tags:
$query->is_404 $query->is_admin $query->is_archive $query->is_attachment $query->is_author $query->is_category $query->is_comments_popup $query->is_comment_feed $query->is_date $query->is_day $query->is_feed $query->is_home $query->is_month $query->is_page $query->is_paged $query->is_posts_page $query->is_post_type_archive $query->is_preview $query->is_robots $query->is_search $query->is_single $query->is_singular $query->is_tag $query->is_tax $query->is_time $query->is_trackback $query->is_year // Methods $query->is_front_page() $query->is_main_query()
Replace other conditional tags with a property check or class method. For example, replace is_front_page() with this method:
if( $query->is_front_page() ){
// This is the front page
}
Or with this check:
if(
$query->is_home
||
( $query->get('page_id') == get_option('page_on_front') )
){
// This is the front page
}
Using is_main_query() inside this hook is not recommended. Use the $query->is_main_query() method instead.
Offsets and pagination
Using the offset argument in any query can break pagination. If you use an offset, modify the query for every pagination page based on the initial offset. See How to use the offset parameter without breaking pagination.
Examples
#1 Include a custom post type in search results
Whether to include a custom post type in search or not is determined when registering the post type; in the arguments of the register_post_type() function: the public=true argument adds the custom post type to the search results.
If the custom post type is not included in search, but you need it to participate in search, then use the following code, analogous to the previous one:
add_action( 'pre_get_posts', 'get_posts_search_filter' );
function get_posts_search_filter( $query ){
if ( ! is_admin() && $query->is_main_query() && $query->is_search ) {
$query->set( 'post_type', [ 'post', 'movie' ] );
}
}
#2 Exclude categories from the home page
This example shows how to remove posts from the specified categories from the output on the blog’s front page. For example, we have 2 categories with IDs 1 and 1347 that we do not need to show on the front page. To exclude these categories from the query, use this code in a plugin or in a theme:
add_action( 'pre_get_posts', 'exclude_category_on_front_page' );
function exclude_category_on_front_page( $query ) {
if ( $query->is_front_page() && $query->is_main_query() ) {
$query->set( 'cat', '-1,-1347' );
}
}
#3 Example WP_Query object
To quickly understand what can be used and how, below is an example of the WP_Query object (the global $wp_query) passed to the hook by reference as $query:
#4 Exclude static pages from search results
When users of your site search for something, often permanent pages may appear in the search results that are, in principle, completely unnecessary in the search results and that can be excluded from search altogether. Use the pre_get_posts hook to exclude permanent pages from search results:
add_action( 'pre_get_posts', 'search_filter' );
function search_filter( $query ){
if( ! is_admin() && $query->is_main_query() && $query->is_search ){
$query->set( 'post_type', 'post' );
}
}
#5 Change the number of posts displayed per page
In WordPress, there is a global setting that determines how many posts to display on a page — posts_per_page. The best way is to change this parameter before the main query, for the sake of resource savings, so that we don’t make repeated queries. Thus, we can use the pre_get_posts action hook to change the number of posts displayed on a page.
This example shows how to override the posts_per_page parameter for the archives page of a custom post type movie:
add_action( 'pre_get_posts', 'hwl_home_pagesize', 1 );
function hwl_home_pagesize( $query ) {
// Exit if this is the admin panel or not the main query.
if( is_admin() || ! $query->is_main_query() )
return;
if( is_home() ){
// Display only 1 post on the home page
$query->set( 'posts_per_page', 1 );
}
// Display 50 posts if this is a post type archive of 'movie'
if( $query->is_post_type_archive('movie') ){
$query->set( 'posts_per_page', 50 );
}
}
Changelog
| Since 2.0.0 | Introduced. |
Where the hook is called
do_action_ref_array( 'pre_get_posts', array( &$this ) );
Where the hook is used in WordPress
add_action( 'pre_get_posts', '_resolve_template_for_new_post' );
remove_filter( 'pre_get_posts', '_resolve_template_for_new_post' );