I've searched for this in the Q&A but I was unable to find it. When I read answers throughout the site I see wp_reset_postdata() placed in multiple areas with the have_posts() conditional and outside the conditional all together. When I read the documentation on wp_reset_postdata() all it states is:

After looping through a separate query, this function restores the $post global to the current post in the main query.

with a snippet of:

// example args
$args = array( 'posts_per_page' => 3 );

// the query
$the_query = new WP_Query( $args );

<?php if ( $the_query->have_posts() ) : ?>

    <!-- start of the secondary loop -->
    <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
        <?php the_title(); ?>
        <?php the_excerpt(); ?>
    <?php endwhile; ?>
    <!-- end of the secondary loop -->

    <!-- put pagination functions here -->

    <!-- reset the main query loop -->
    <?php wp_reset_postdata(); ?>

<?php else:  ?>

    <p><?php _e( 'Sorry, no posts matched your criteria.' ); ?></p>

<?php endif; ?>

but when I reference other ways, such as "How to fix pagination for custom loops?" it's after the conditional:

// Output custom query loop
if ( $custom_query->have_posts() ) :
    while ( $custom_query->have_posts() ) :
        // Loop output goes here
// Reset postdata

Wouldn't it be appropriate to have it afterwards and not within the have_posts() conditional because if you do use an else statement the arguments aren't being reset if you do not have posts. So my question is where should wp_reset_postdata() go?

  • Don't forget to reset your query when you're completely done with it using wp_reset_query(); May 24 '17 at 16:04

Short version:

As Tom J Nowell said,

You shouldn't cleanup if there's nothing to clean

Long version:

However, if you put wp_reset_postdata() after (or outside) the loop, it would still work perfectly fine. I have used the function in various scenarios, including something like this:

dynamic_sidebar( 'main-sidebar' );

The reason was that some widget was querying posts without cleaning after itself.

Just keep in mind which query you want to reset. In Tom's example, no matter if wp_reset_postdata is within the if statement or not, once it gets called, you will have problems because it will directly go back to the main post instead of the parent custom loop.

What the function does is tell a WP_Query object to restore the current post to the global scope. Since it's a function instead of a method, then there is a single object it works with - the global $wp_query instance of the WP_Query object.

If you have custom loops, where you use a newly generated WP_Query, you should use the reset_postdata method of that query:

$pages = new WP_Query( 'post_type=page' );
while( $pages->have_posts() ) {

    // Page title
    echo '<h1>'; the_title();  echo '</h1>';

    $books = new WP_Query( 'post_type=book&...' );
    if( $books->have_posts() ) {
        while( $books->have_posts() ) {

            // Book title
            echo '<li>'; the_title(); echo '</li>';

        // Don't go back to the global post, go back to the "page"
        // wp_reset_postdata();

    // Page content

// Finally, reset the main query

Hope this sums it up :)


You did it correctly! It's the example that's wrong. Sadly almost all examples of queries have this problem

Normally putting it after the conditional has no ill effects, but when nesting loops it could cause problems in edge cases. For 99.9% of use cases this isn't an issue.

By putting it inside the conditional you've ensured the the postdata is only reset if it was changed.

Consider this edge case:

while( have_posts() ) {
    ... stuff...
    $q = new WP_Query( $args );
    if ( $q->have_posts() ) {
        while( $q->have_posts() ) {
            $related_posts = new WP_Query( $args2 );
            if ( $related_posts->have_posts() ) {
                while( $related_posts->have_posts() ) {


Here we're doing a nested loop to fetch related posts, but notice what happens if no related posts are found and $related_posts->have_posts() returns false, the postdata is reset back to that of the main loop, and you get the main queries post content, not that of the $q query


Normally I use it outside of if($query->have_posts()) statement. This way you can make sure that even if there is no post, then it will reset the query. I don't have any problem by placing it after (and outside) of if($query->have_posts()) statement.

  • 2
    You shouldn't cleanup if there's nothing to clean, you might be cleaning up after another queries work before it's finished with it
    – Tom J Nowell
    May 24 '17 at 14:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.