DiggingIntoWordPress

by Chris Coyier & Jeff Starr

4 Ways to Loop with WordPress

Posted by on

At the heart of the WordPress theme template is the venerable WordPress loop. When you’re looking at your index.php file, for example, the loop is the part that typically begins with if(have_posts()) and contains all the tags and markup used to generate the page. The default loop works perfectly well for most single-loop themes, but for more advanced designs with stuff like multiple and custom loops, more looping power is needed. Fortunately, WordPress provides plenty of flexibility with four ways to loop:

Each of these looping methods is useful in a variety of situations. They share a lot of the same underlying functionality, and the query parameters are essentially the same. Collectively, these four techniques enable simple loops, multiple loops, and custom loops in your WordPress theme template. A good place to find a default loop, for example, is in your theme’s index.php file. Its purpose is to loop through the posts stored in the database and echo their contents to the browser. Using WordPress’ template tags, it’s easy to display post titles, content, meta info, and much more. That said, let’s examine the four ways to loop with WordPress.

The Default Loop

The default WordPress loop looks something like this:

<?php if (have_posts()) : while (have_posts()) : the_post(); ?>

	<div <?php post_class(); ?> id="post-<?php the_ID(); ?>">
		<h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
		<?php the_content(); ?>
	</div>

<?php endwhile; ?>

	<div class="navigation">
		<div class="next-posts"><?php next_posts_link(); ?></div>
		<div class="prev-posts"><?php previous_posts_link(); ?></div>
	</div>

<?php else : ?>

	<div <?php post_class(); ?> id="post-<?php the_ID(); ?>">
		<h1>Not Found</h1>
	</div>

<?php endif; ?>

So what makes it “default”? Mostly because it uses the default query to loop through post content, making it the loop used like 99% of the time for most themes. It tells WordPress to loop through posts and display the information according to context, and as called by the various template tags (the_title, the_content, et al). There are tags available for just about any type of data stored in the database.

Based on the query that is sent, the default loop will display a certain number of posts from a certain category from a certain date, and so on. For example, the number of posts displayed in the first part of the loop is specified in the WP Admin. So if someone requests the second page of your “Awesome” category, that information is sent via the query, along with the number of posts, theme template file, and so on.

So the default loop is perfect if you’re happy with the query that is sent, but it is also possible to customize the query and generate an entirely different set of posts.

Loop with query_posts()

The query_posts function enables us to modify the query and display our desired results. We can either override the entire query or keep it around and just change a few parameters. Here’s an example where query_posts is called before the default loop to exclude a specific category:

<?php global $query_string; // required
$posts = query_posts($query_string.'&cat=-9'); // exclude category 9 ?>

<?php // DEFAULT LOOP GOES HERE ?>

<?php wp_reset_query(); // reset the query ?>

Say you have a default loop in your index.php theme template, but you want to change the number of posts, exclude two categories, and display the results in ascending order. Easy. Just add some query_posts action before the default loop and wp_reset_query immediately after, like this:

<?php global $query_string; // required
$posts = query_posts($query_string.'&posts_per_page=3&cat=-6,-9&order=ASC'); ?>

<?php // DEFAULT LOOP GOES HERE ?>

<?php wp_reset_query(); // reset the query ?>

Here we are keeping the original query around and just overriding a few parameters. There are many parameters available, so customizing any default loop is accomplished quite easily. If we wanted to completely override the original query, we would replace the second line with something like this:

$posts = query_posts('posts_per_page=3&cat=-6,-9&order=ASC');

Notice here that we’ve removed the $query_string from the query_posts parameters. This essentially erases the default query and replaces it with one that contains only those variables included in query_posts. This means no paging information will be available, so remove the original query only if you know what you’re doing.

When to use? Use query_posts to modify the type of posts that are returned for a single loop. It’s perfect for limiting the number of posts, excluding posts from a certain category or tag, and so on. If more than one loop is required, multiple query_posts loops could work, but there is a much better way to do it using WP_Query.

Loop with WP_Query()

For complete control over the customization of any number of loops, WP_Query is the way to go. When used to modify a default loop, it looks similar to query_posts. For example, let’s exclude a specific category using WP_Query:

<?php $custom_query = new WP_Query('cat=-9'); // exclude category 9
while($custom_query->have_posts()) : $custom_query->the_post(); ?>

	<div <?php post_class(); ?> id="post-<?php the_ID(); ?>">
		<h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
		<?php the_content(); ?>
	</div>

<?php endwhile; ?>
<?php wp_reset_postdata(); // reset the query ?>

It also accepts the same parameters as query_posts, so modifying stuff like number of posts, included/excluded categories, and post order looks quite familiar. As seen in the following examples, WP_Query makes it easy to customize the loop by simply changing the parameter:

$custom_query = new WP_Query('cat=-7,-8,-9'); // exclude any categories
$custom_query = new WP_Query('posts_per_page=3'); // limit number of posts
$custom_query = new WP_Query('order=ASC'); // reverse the post order

As expected, we can combine parameters with WP_Query using the same syntax as both query_posts and get_posts. Here’s the equivalent of our query_posts example:

$custom_query = new WP_Query('posts_per_page=3&cat=-6,-9&order=ASC');

Notice, however, that with WP_Query, we don’t need the $query_string variable. In addition to using WP_Query to customize the default loop, we can also use it to create and customize multiple loops. Here is a basic example:

<?php // Loop 1
$first_query = new WP_Query('cat=-1'); // exclude category
while($first_query->have_posts()) : $first_query->the_post();
...
endwhile;
wp_reset_postdata();

// Loop 2
$second_query = new WP_Query('cat=-2'); // exclude category
while($second_query->have_posts()) : $second_query->the_post();
...
endwhile;
wp_reset_postdata();

// Loop 3
$third_query = new WP_Query('cat=-3'); // exclude category
while($third_query->have_posts()) : $third_query->the_post();
...
endwhile;
wp_reset_postdata();
?>

Each of these additional loops may be placed anywhere in your theme template – no need to line them up sequentially. For example, one loop may be placed in your sidebar, another in your footer, and so on. And with the output of each loop easily modified using any of the available parameters, any loop configuration is possible.

When to use? Use WP_Query for creating multiple, customized loops. By setting up additional instances of WP_Query in your theme, you can create any number of multiple loops, and customize the output of each.

Even so, we don’t always need to break out the big guns, sometimes we just need a few additional loops displayed around the page. So let’s put down the bazookas and gather in the garden for some get_posts tea ;)

Loop with get_posts()

The easiest, safest way to create multiple loops in your theme is to use get_posts(). Anywhere you need to display a quick, static set of posts, get_posts is the perfect choice. Think 10 recent posts in the sidebar, or 10 random posts in the footer. get_posts makes it easy. Here again is a query to exclude a specific category:

<?php global $post; // required
$args = array('category' => -9); // exclude category 9
$custom_posts = get_posts($args);
foreach($custom_posts as $post) : setup_postdata($post);
...
endforeach;
?>

This code creates a loop of all posts except those in the excluded category. Of course, excluding a category is just one way to customize your additional, static loops. By using any of the same parameters accepted by WP_Query and query_posts, it’s possible to create loops that display just about anything you want.

Notice, however, that get_posts requires the use of an array for the parameters. The format for multiple parameters looks like this (using our previous example):

$args = array('numberposts'=>3, 'category'=>-6,-9, 'order'=>'ASC');

Also notice that we’re using numberposts instead of posts_per_page to limit the number of posts. According to the WP Codex, posts_per_page should work with get_posts, but if it doesn’t just go with numberposts. There is also a showposts parameter that also seems to work fine with get_posts.

When to use? Use the get_posts() function to easily create additional, static loops anywhere in your theme. get_posts accepts the same parameters as query_posts, and is perfect for adding static, custom loops to your sidebar, footer, or anywhere else.

30-Second Summary

The bottom line for customizing default loops and creating multiple loops:

  • To modify the default loop, use query_posts()
  • To modify loops and/or create multiple loops, use WP_Query()
  • To create static, additional loops, use get_posts()

If you’re working with WordPress loops and want to learn more about using them to customize your theme, we cover the topic extensively in our book, Digging into WordPress, which is current with WordPress 3.1 :)

34 Responses

  1. Justin May 11, 2011

    This is pretty funny you just posted this information. I used both WP_Query and query_posts the other day for one of my clients.

    Great information! Keep up the great work.

  2. Matthew Muro May 11, 2011

    I think a good rule of thumb is that WP_Query is the safest to use anywhere because it does not affect the main loop in the way that query_posts does.

  3. Awesome resource – will be directing a lot of people this way!

  4. I’ve been wanting someone to explain this for years! Thanks for clearing up my confusion (finally).

  5. Jµ May 11, 2011

    Which is the best to use: wp_reset_query(); or wp_reset_postdata(); ?

    • Jeff Starr

      They do different things, so it depends on the situation:

      wp_reset_query destroys the previous query and sets up a new query using wp_reset_postdata. Best used with query_posts to reset things after a custom query.

      wp_reset_postdata restores global $post variable to the current post in the default query. Best used with WP_Query.

      • Julien May 11, 2011

        Thanks for the tip ! I was using wp_reset_query with WP_query until now…

  6. for those who -still- don’t own it, this is an excerpt from the DIWP book/eBook ;)

    Thanks Jeff :)

  7. Boomboom May 12, 2011

    Thanks for the tip. I read it the other day from the e-book. I have used all the above at different points of time but have remained forever confused as to the difference. Your book clears it up for me and now having a blog post is also cool :)

    PS – Just got a note for USPS for the physical book. I am so excited :)

  8. Thank you so much for this. Perfect timing as this answers a problem I’ve had for a couple of days.

  9. Nathan B May 12, 2011

    very helpful, these have been muddled in my mind. Thanks!

  10. Dragon May 12, 2011

    A bit off the point, but would you know the best way to prevent the default loop from executing?

  11. THANK YOU THANK YOU THANK YOU! This is very well crafted and written in a way that really cleared up the fog in my head re: loops and when to use which. Much appreciated and bookmarked!

  12. What about custom posts? Which is best for multiple custom post loops on a single page? Seems like WP_Query.
    Thoughts from the geniuses?

    • Jeff Starr

      Yes WP_Query is the way to go for multiple custom loops, as described in the article ;)

  13. You’ve made WordPress loops fun again for me. Thanks, Jeff.

  14. Thanks. Very valuable!

  15. Ivaylo Draganov May 22, 2011

    To create static, additional loops, use get_posts()

    Wish I had this article when I was learning how to loop. But what do you mean by saying ‘static’ loops? What’s the advantage of using get_posts over WP_Query for simple sidebar/footer loops?

  16. Vassilis Mastorostergios May 22, 2011

    Although this is an unbelievably useful post for people starting out what I’d really expect/like to see is a more in depth analysis of each looping method. For example, why is WP_Query preferred for multiple loops? Why not use query_posts? Why is get_posts faster and simpler, it seems to use more code, a more difficult syntax and what are static posts? How many calls to the database does each loop make and which one would you use for really high end projects? How are these looping methods different and why?

    • hoss9009 May 22, 2011

      Here, here…

    • Jeff Starr

      Lots of great questions! I wish I had time to go deep, but I can say that by static posts I’m referring to posts data that’s displayed one time only, like in the sidebar, and with no paging options or post navigation. I hope that helps!

    • Rarst May 23, 2011

      For example, why is WP_Query preferred for multiple loops? Why not use query_posts?

      query_posts() modifies global $wp_query variable, which absolutely should not be done unless you need exactly that.

      Why is get_posts faster and simpler, it seems to use more code, a more difficult syntax…

      Simply put get_posts() is wrapper around WP_Query. It is simpler in usage because it’s more straightforward – you query and get array of posts, nothing more.

      How many calls to the database does each loop make and which one would you use for really high end projects?

      Any loop boils down to instance of WP_Query object and same methods to fetch info from database.

      • Grant Z July 22, 2011

        Rarst, that’s one awesome chart. Thanks for sharing!

  17. hoss9009 May 23, 2011

    I ran into prob w/ the loop and CPTs.
    SOLVED (I COMMENTED OUT MY OLD LOOP):

    <?php //$loop = new WP_Query(array('post_type'=>'web','posts_per_page'=>6)); ?>
    <?php //while ( $loop->have_posts() ) : $loop->the_post(); ?>
    <?php $paged = (get_query_var('paged')) ? get_query_var('paged') : 1; ?>
    <?php query_posts('post_type=web&posts_per_page=6&paged='.$paged); if(have_posts()) : ?>
    <?php while (have_posts()) : the_post(); ?>
    <div class="portfolio-item">
    <div class="portfolio-thumb"><a href="<?php the_permalink() ?>" rel="bookmark" title="Portfolio item: <?php the_title_attribute(); ?>"><?php the_post_thumbnail('portfolio-thumb'); ?></a></div>
    <h4 class="portfolio-title"><a href="<?php the_permalink() ?>" rel="bookmark" title="Portfolio item: <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h4>
    </div>
    <?php endwhile; ?><?php wp_pagenavi(); ?>
    <?php //wp_reset_postdata(); // reset the query ?>
    <?php endif; ?>

    I’m not a WP expert so I’m not 100% sure how get_query_var works.

    Thoughts?

    PS. Pagination was so tough to get to work!

  18. Mikey May 29, 2011

    How to exclude tag from the loop?
    I’m trying this :

    new WP_Query('posts_per_page=3&order=ASC');

    but it doesn’t work, where am I wrong?

  19. Mikey May 29, 2011

    sorry, this is the query I wrote :

    posts_per_page=3&tag=-13&order=ASC

  20. One thing you missed is why you would loop in the first place. I read the article, I get the coding but I still don’t know why I need to do this or what advantage it gives me. A short little intro paragraph would make the difference to your article.

  21. hoss9009 June 1, 2011

    How would I show only on category from my custom taxonomy? Here’s my loop:

    <?php $loop = new WP_Query( array( 'post_type' => 'news', 'posts_per_page' => 5 ) ); ?>
    <?php while ( $loop->have_posts() ) : $loop->

    My custom taxonomy is named “categories” (boring, I know).

    Would it looks something like this?

    <?php $loop = new WP_Query( array( 'post_type' => 'news', 'posts_per_page' => 5, 'taxonomy' => 'categories' ) ); ?>
    <?php while ( $loop->have_posts() ) : $loop->

    I’m sure it can’t be that easy….

    Thanks! I love this article!

    • hoss9009 June 1, 2011

      Sorry I guess I would need the name of the category.

      Maybe like this?

      <?php $loop = new WP_Query( array( 'post_type' => 'news', 'posts_per_page' => 5 , 'taxonomy'=>'slideshow') ); ?>
      <?php while ( $loop->have_posts() ) : $loop->

  22. This was a very helpful article. I used WP_Query to set up two loops, one to display posts in a category (News) and the other to display a custom post type underneath it (Events). My only problem is that I’d like to put a subhead for events just like I have for news, but I get an error when I simply put in Events in between the loops. What’s the proper way to do that? Here’s my template:

    <?php /* Template Name: News and Events */
    get_header(); ?>

    <div id="container">
    <h2>News</h2>

    <?php // Loop 1
    $first_query = new WP_Query('posts_per_page=10&cat=3'); //News
    while($first_query->have_posts()) : $first_query->the_post();

    the_title( '<h3 class="entry-title"><a href="' . get_permalink() . '" title="' . the_title_attribute( 'echo=0' ) . '" rel="bookmark">', '</a></h3>' );

    endwhile;
    wp_reset_postdata();

    // Loop 2
    $second_query = new WP_Query( array( 'post_type' => 'events', 'posts_per_page' => 10 ) ); //Events
    while($second_query->have_posts()) : $second_query->the_post();

    the_title( '<h3 class="entry-title"><a href="' . get_permalink() . '" title="' . the_title_attribute( 'echo=0' ) . '" rel="bookmark">', '</a></h3>' );

    endwhile;
    wp_reset_postdata();
    ?>

    </div>

    <?php get_sidebar(); ?>
    <?php get_footer(); ?>

    </div>
    </body>
    </html>

  23. pakpenyo July 10, 2011

    How about loop for custom taxonomies, with latest post from taxonomy category but the post permalink is linked to taxonomy category?

    ...loop
    <a href="<?php the_permalink() ?>" title="<?php the_permalink() ?>">
    <?php the_title(); ?>
    </a>

    change to

    ...loop
    <a href="LINK to TAXONOMY CATEGORY>" title="<?php the_permalink() ?>">
    <?php the_title(); ?>
    </a>

    Thanks

  24. Dominor Novus June 14, 2012

    Fantastically explained/distinguished loops. Very useful, thanks. I found WP_Query to be the most suitable.

  25. How can I include in the WP_query? it’s not displaying older posts when clicking it…
    i add:
    endwhile;
    posts_nav_link();
    wp_reset_postdata();

    but its not working for some reason

  26. TonyaOD July 27, 2012

    In your basic loop you have some class styles defined. Where can I find what you have in these styles. Since I dont have them in my css it wont show what it should

Comments are closed. Contact us with any critical information. Thank you!

Code is poetry