• Home
  • blog
  • Mastering WP_Query: Using the Loop

Mastering WP_Query: Using the Loop

Category: Uncategorized

As I outlined in the introduction to this series, the WP_Query class has four main elements:

  • the arguments for the query, using parameters which will be covered in detail in this series
  • the query itself
  • the loop, which will output post content, titles or whatever you want to display
  • finishing off: closing if and while tags and resetting post data

In this tutorial I’ll show you how to use the loop with WP_Query, including the two main ways to structure your loop and how to use multiple loops.

Where the Loop Fits

Without a loop, nothing will be displayed on your page. After WordPress has run the query, using the arguments you’ve defined, it then needs to be told what to output from the data that it’s fetched. This is where the loop comes in.

So the loop comes after your query, and it uses three tags:

  • if( $query->have_posts() ) checks if there are any posts.
  • while( $query->have_posts() ) repeats the loop for each post as long as there are posts to retrieve.
  • $query->the_post() accesses that specific post.

So this is where the loop fits in the WP_Query class:

After running the loop, all that’s left to do is tidy things up using wp_reset_postdata().

Structure of the Loop

The way your loop is structured will depend on what data you want to display from your post. Here’s an example loop which outputs the post title, featured image and an excerpt. You’d use a loop like this on an archive page.

This loop displays exactly what I’ve described above: the featured image, title and excerpt.

Taking the Loop Further: Checking For Content

But sometimes you might want to add a heading before your list of posts, or you might want to enclose them all in a containing element. If you simply added this before your loop, it would be output regardless of whether the query actually returned any data, meaning you could have a heading with nothing beneath it, or some unnecessary markup.

This is very easy to get around by putting the enclosing element or the heading inside your if tag:

Here you can see I’ve checked if there are any posts retrieved by my query, and if there are I’ve opened a containing element and added a heading. 

This is also useful if you want to output the results of your query as a list. Let’s say I want to create a list of all posts in a given category. The ul element isn’t inside my loop as it doesn’t relate to one specific post, but I only want to output it if there are posts. So I use this:

This checks if the query has fetched any posts, and if so, it opens the ul element and then runs the loop.

Running Extra Loops

It’s important to be aware that while you can use WP_Query to run more than one loop, you have to reset the post data and start a second instance of WP_Query to do this. This is because each of your loops will be outputting data based on different arguments.

This example displays the excerpt and featured image for the first post and then just the title of each subsequent post:

I’ve used two key arguments here:

  • 'posts_per_page' => '1', used with the first query, outputs just the most recent post.
  • 'offset' = '1', used with the second query, skips the first post, ensuring it’s not repeated in the list below.
  • As you can see from the code above, the loop is slightly different for each query. The first one outputs the featured image, title and excerpt, while the second checks if the query has posts and if so, opens a ul element and encloses each post title in a li element and a link to its page.

You’ll also notice that I used wp_reset_postdata() after both loops. If I hadn’t done this, the second loop would still output data from the first.


Without a loop, WP_Query doesn’t really do very much. The loop is the code you use to display the data that WordPress has fetched from the database based on your query arguments.

As I’ve demonstrated, there are a few variations on the loop. A simple loop will just output all posts in the order you’ve specified in your query arguments (or by date in descending order by default). If you separate if( $query->have_posts() ) and while( $query->have_posts() ), you can insert additional markup outside your loop, but only if your query has returned data. And finally, by specifying alternative arguments and using wp_reset_postdata() after each loop, you can use WP_Query more than once to create multiple loops on your page.