This free WPShout Course is a step-by-step introduction to WP_Query, one of the most powerful systems in WordPess. By the end of the course, you’ll know what a WP_Query
is, and how and why to use your own custom WP_Query
s for theme and plugin development. It really is a revolutionary and powerful tool for every WordPress theme and plugin developer to master 🙂
One thing before we dive in. If you want access to videos and additional code examples about WP_Query
—and if you want to better understand WordPress development in general—have a look at our full “learn WordPress development” course, Up and Running. It’s the best guide to WordPress development out there.
The Best Way to Learn WordPress Development
Get Up and Running Today

Here’s what they have to say:
“I think anyone interested in learning WordPress development NEEDS this course. Watching the videos was like a bunch of lights being turned on.” -Jason, WordPress developer
“Other courses I’ve tried nearly always lack clear explanations for why WordPress does things a certain way, or how things work together. Up and Running does all of this, and everything is explained clearly and in easy-to-understand language.” -Caroline, WordPress freelancer
1. WP_Query
: Understanding the Basics of Object-Oriented Programming
The WP_Query
API uses a programming style called object-oriented programming, or OOP. Every WP_Query
is actually a WP_Query
Object, meaning an individual instance of the broader WP_Widget
class. If this setup doesn’t make sense to you, you’ll want to quickly learn the basics of object-oriented programming (OOP):
Introduction to Object-Oriented PHP for WordPress Developers
Key Point: Classes and Objects
A class is an abstract description of a type of thing. Once you’ve defined a class in object-oriented code, you create actual objects which contains the traits you defined in the class. The article uses the example of a Chair class:
// Creating the Chair class
class Chair {
// Here we list important elements that all Chairs have,
// like "height" and "color"
}
// Creating a Chair object
$mychair = new Chair;
This is the bedrock of what you’ll need to know in OOP to work with WP_Query
.
2. The Loop: How WordPress Processes Fetched Post Bundles into Webpages
WP_Query
does precisely one thing: it fetches bundles of WordPress posts from the WordPress database. Before you learn WP_Query
directly, you’ll need to understand the basic engine of WordPress: the Loop, which takes these bundles of fetched posts, and processes them, one after the other, into the contents of your site’s pages.
Key Point: A Basic Loop
The Loop works on a fetched bundle of posts, and it processes through them one by one. This is what while ( have_posts() )
means: as long as there are posts left in the bundle to be processed, then continue processing them one by one.
The processing is done inside the while
loop. This is where most HTML markup goes, as well as template tags that rely on the Loop, like the_title()
.
A simple, working Loop like you might see on your theme’s index.php
looks like this:
<?php if ( have_posts() ) :
while ( have_posts() ) :
the_post(); ?>
<article class="full-article">
<h2><?php the_title(); ?></h2>
<?php the_content(); ?>
</article>
<?php endwhile;
endif;
The Loop is how WordPress processes post bundles into webpages. The next articles, on WP_Query
and related topics, cover how to fetch and work with these post bundles.
3. The Basic Use of WP_Query
This article covers the standard use of WP_Query
: writing custom queries by creating new WP_Query
objects with custom $args
, and then looping through those custom queries to create the desired page output.
Key Point: Creating a WP_Query
with Custom $args
The power of creating a WP_Query
object is in the custom arguments you pass into the creation process. These arguments are best written as an associative array, which is often given the standard name $args
.
The following code defines three arguments that will be used to create a new WP_Query
. This query will retrieve:
- The first 10 posts,
- Whose author is the user
fred
, - Whose post type is
post
.
$args = array(
'posts_per_page' => 10,
'author_name' => 'fred',
'post_type' => 'post',
);
$query = new WP_Query( $args );
The end result of this process is that the value of the variable $query
is now the fetched post bundle: a PHP object with the complete content and metadata of the ten posts that our WP_Query
retrieved.
Key Point: Looping through a Custom WP_Query
The process we use to work with the fetched post bundle stored in $query
is almost identical to the Loop. The following custom loop will print out the titles of the ten fetched posts, one by one (and then reset the query with wp_reset_postdata()
to avoid errors later in processing):
while( $query->have_posts() ) {
$query->the_post();
echo '<li>' . get_the_title() . '</li>';
}
// Restore original Post Data
wp_reset_postdata();
4. Using WP_Query
Objects Without the Loop
This section explores WP_Query
objects not as a mysterious things we can loop through, but as PHP objects with well-defined properties. Being able to work with WP_Query
objects directly makes some things possible that you can’t do with standard loops.
Key Point: Working with WP_Query
Objects Directly
A WP_Query
object has a defined structure, and defined properties and methods. For example, for a WP_Query
called $query
, you can directly access the array of fetched posts with $query->posts
, you can get the post title of the first fetched post with $query->posts[0]->post_title
, and so on.
Key Point: Using Functional Operations to Modify WP_Query
Objects
The example in this article sorts fetched posts alphabetically by post content (not an option with regular custom queries and loops) with sorting methodologies from basic functional programming, including array_map()
and usort()
. You can learn these functions in the article, and in our introduction to functional PHP for WordPress developers.
5. Modifying Existing WP_Query
s with pre_get_posts
pre_get_posts
is a filter that makes it possible to alter the parameters of an existing WP_Query
, before it fetches its post bundle from the WordPress database—thereby changing the post bundle it ultimately fetches. It is more convenient than writing a custom WP_Query
in some situations, and in others it’s virtually the only path to a working solution.
Key Point: How to Modify an Existing Query with pre_get_posts
pre_get_posts
is a WordPress filter that fires immediately before a WP_Query
object fetches posts from the WordPress database. Using this filter makes it possible to modify the query directly.
The following example changes the post type that will display on a site’s blog index from post
to page
:
add_filter( 'pre_get_posts', 'wpshout_pages_blogindex' );
function wpshout_pages_blogindex( $query ) {
if ( is_home() && $query->is_main_query() ) :
$query->set( 'post_type', 'page' );
endif;
}
Key Point: Use pre_get_posts
to Alter WordPress Default Queries
WordPress’s default queries, like the ones that create your blog index and your archive pages, are difficult to alter without pre_get_posts
. Changing these default queries is the primary use case for pre_get_posts
.
The following real-world example alters the main queries that run on archive pages. These queries will now fetch posts of a new custom post type, course
, alongside the post
results they normally return:
add_filter( 'pre_get_posts', 'wpshout_add_custom_post_types_to_query' );
function wpshout_add_custom_post_types_to_query( $query ) {
if( is_archive() &&
$query->is_main_query() &&
empty( $query->query_vars['suppress_filters'] )
) {
$query->set( 'post_type', array( 'post', 'course' ) );
}
}
We’ve Only Just Begun… to Know WP_Query
I hope you feel like you now understand what the WP_Query
class and and objects are, how you can use them to get just about any content you want out of WordPress. There are so many more selection criteria available than we’ve touched on so far to. Chances are good that if you ever think “I’d like to get posts where…” a trip to the Codex to find out the specific way that works inside WP_Query
will get you going. You can filter by any taxonomy, any custom field, any post status, and even whether or not the post is password protected.
While we’ve talked about many things, we’ve rarely not covered many (or any) of the nerdy intricacies of the way WordPress uses the WP_Query
class. If you’re ready for a bit more depth, I recommend highly this talk from Andrew Nacin: “You Don’t Know Query.” (I discovered that from the great post from Brian Krogsgard.)
Thanks for reading, and as always, we’re happy to hear questions and comments below!
Can you please point us to documentation on the query_vars property?
Thanks,
Don
But where to use this wp_query?. I create a PHP file inside the wp-content and just go to the address of the php file to call it?
Question from a noob: You can use get_posts instead of wp_query to fetch the first 10 posts by author Fred, right?
I’ve been using wp_query for a while, and it was only recently that I discovered get_posts, which seems to be better for performance in the some simple uses.
Bookmarked! Tweeted! Than you for creating such a clear resource
Thanks for sharing 🙂
thanks this was the best tutorial i ever saw 🙂
Well written, well explained, the best walk trough the wp_query I’ve seen, thanks.
🙂
I love the way you organize the content, interspersing related resources for full ground up understanding of the topic. I read a lot of training resources and I feel yours is the best by far. Great use of font, imagery and content! <3 20 million likes! ^_^