WP_Query: Understand it, Love it!

wp_query header image

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_Querys 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

Up and Running is our complete “learn WordPress development” course. Now in its updated and expanded Third Edition, it’s helped hundreds of happy buyers learn WordPress development the fast, smart, and thorough way.

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.

Understanding The Loop: WordPress’s Way of Showing Posts

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(); ?>

	<?php endwhile;

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.

Getting to Know WP_Query

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:

  1. The first 10 posts,
  2. Whose author is the user fred,
  3. 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() ) {
	echo '<li>' . get_the_title() . '</li>';

// Restore original Post Data

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.

Using WP_Query Objects Without the Loop

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_Querys 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.

Practical Uses of pre_get_posts

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' );

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!

Image credit: pixabay

Most Voted
Newest Oldest
Inline Feedbacks
View all comments
January 15, 2020 12:42 pm

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! ^_^

January 11, 2020 5:23 pm

Can you please point us to documentation on the query_vars property?


senthil kumar
December 13, 2019 10:21 pm

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?

July 29, 2019 3:57 am

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.

Hashim Warren
October 20, 2018 9:45 am

Bookmarked! Tweeted! Than you for creating such a clear resource

David Hayes
October 24, 2018 2:26 pm
Reply to  Hashim Warren

Thanks for sharing 🙂

December 14, 2016 2:09 am

thanks this was the best tutorial i ever saw 🙂

Holger Govertz
July 12, 2016 3:33 pm

Well written, well explained, the best walk trough the wp_query I’ve seen, thanks.

July 26, 2016 4:13 pm
Reply to  Holger Govertz