Understanding WordPress loop

WordPress loop determines what content to fetch from database depending upon the page you are visiting (i.e., the SQL query is generated based on the URL value).


  • If a user visits https://bharatt.com.np/category/wordpress then WordPress will know user is viewing a category and load the appropriate template, select the posts saved with that category, and generate the output for the page.
  • parse_query method within WP_Query object is responsible for translating URL into set of query parameters.
  • get_posts method within WP_Query object takes query parameters and converts them into a SQL statement and eventually fetching the content from database.
  • Fetched content is saved into WP_Query object and cached so that future references to the same query won’t generate additional database traffic.
  • Once content is retrieved and saved into WP_Query object, WordPress sets all the conditional tags i.e., is_* like; is_home(), is_page(), is_singular().
conditional tags
  • To learn more, you can view the files:
    • wp-includes/class-wp-query.php
    • wp-includes/query.php
    • wp-includes/taxonomy.php

Working with WordPress loop

The loop, by default, is used in theme templates. Loops can be used anywhere inside of WordPress, but different methods exist for creating custom loop depending on where they are used, and the potential side effects of each loop construction will differ.

Two types of loop:

  • The default loop: it is based on URL
  • Custom loop: your custom database query
// standard loop example.
if( have_posts() ):
    while ( have_posts() ):
        //loop content (template tags, html, etc)
  // custom 404 message.

Explanation of the loop:

  • First the loop checks if there is any content to loop through.
  • If the content exists, the while statement is used to cycle through all the posts.
  • the_post() is called to build the post data. This function must be called inside the loop to setup the post data correctly. Calling the_post() in turn calls the setup_postdata() function to setup the post metadata such as author, tags, categories, content. This data is assigned to a global variable $post each time through the loop iteration.
  • Setting up the post data also applies the appropriate filters to the raw content that comes out of the database. This basically means any plugin can modify the post content before outputting on the page.
  • This is the default loop and by the time this default loop is called, WordPress has already called the get_posts() method within the default query object to build the list of appropriate content for the URL being viewed.

Template tags

  • Built-in WordPress functions to get contents from the database or display the content inside of a Loop.
  • Template tags can be used inside and outside of the loop but you may need to supply additional arguments when using outside of the loop.
  • Template tags can have parameters that can be used to modify the output. Visit Template Tags for more information.

Customizing the loop

  • Two ways you can customize the loop
    • using pre_get_posts hook. This method is best to alter main query.
    • by instantiating new WP_Query object
  • Other two less used ways to get customized data
    • get_posts($args) : documentation
    • query_posts($args) : documentation
    • query_posts alters the main query hence its not recommended to use in main loop.

1. Building WP_Query object

  • Custom loops can be used anywhere on theme template files to display different types of content thus they must build on separate instance of WP_Query variable.
  • The key to building a powerful custom loop is to map content selection criteria into the right set of query parameters.
  • Parameters are used to define what content will be returned in your loop (main or custom). See the documentation for full list of supported parameters.
// adding paging to custom loop
Note: Paging is currently designed to work only with the $wp_query global variable; that is, it works within the default Loop and requires some sleight of hand to make it work in custom Loops. You need to trick WordPress into thinking your custom query is actually $wp_query in order for paging to work. 

$temp = $wp_query; // original state is saved in $temp.
$wp_query = null; // reset to null for instantiating new object.

$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;

$args = [
    'paged' => $paged,
$wp_query = new WP_Query( $args );

while( $wp_query->have_posts() ): $wp_query->the_post();
    // loop body

$wp_query = null;
$wp_query = $temp; // back to its original state.

2. Using pre_get_posts hook

  • This hook allows you to modify any loop query existing in your theme templates.
  • Generally preferred method for modifying the main/default loop.
  • pre_get_posts hook accepts the global WordPress query by reference which enables you to modify the query variables prior to running the query i.e., fetching the database content.
  • Visit documentation page for more information.
function custom_selection_criteria( $query ){
    if ( ! is_admin() && $query->is_home() && $query->is_main_query() ) {
        $query->set( 'category_name', 'category-slug' );
add_action( 'pre_get_posts', 'prowp_exclude_category' );

>> query is not for admin screens.
>> query applies only on home page
>> query is for main loop only.

Notes on using pre_get_posts hook:

  • Targeting right query through the use of conditional tags is essential.
  • When using conditional tags, always reference them via the passed instance of $query and don’t use tags like is_front_page() because such tag reference global instance of $wp_query not the passed one.

Multiple Loop

Case 1: when you don’t need to alter the query parameters

  • In this case, you can run multiple loops in a template with the help of rewind_posts() function.
// Start the main loop
if ( have_posts() ) : 
    while ( have_posts() ) : the_post();
// Start a new loop
while ( have_posts() ) : the_post();

// we are referencing same query twice and first loop contains same data as second loop.

Case 2: when you need to alter the query parameters for extracting different content

  • in such situation, you will need to reset the first query and initialize again by instantiating new WP_Query object.
// The main query. (e.g., on single page)

if ( have_posts() ) : 
    while ( have_posts() ) : the_post();
        // loop body
        // single post content and meta information
else :
    // 404 template
 * The secondary query.
 * Display related posts on single page (category as reference)
$categories = get_the_categories();
$args = [
    'posts_per_page' => 4,
    'post__not_in' => [get_the_ID()],
    'cat'          => !empty( $categories ) ? $categories[0]->term_id : NULL,
$related_posts = new WP_Query( $args );
// The second loop. 
if ( $related_posts->have_posts() ):
    while ( $related_posts->have_posts() ) : $related_posts->the_post();
        // loop body.
    // no related posts in this category!

Resetting the post data

  • When using multiple loops, you will need to reset the post data once the loop is completed. Not doing so can lead to unexpected results.
  • Three ways to reset post data:
    • wp_reset_postdata(): use it when you are referencing WP_Query object.
    • wp_reset_query() : use it when you are using query_posts($args) to get the content.
    • rewind_posts() : use it when you are in main loop.

Reference: WordPress Loop & WP_Query Class

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.