WP_Query & The Loop
WP_Query is the class WordPress uses to retrieve posts from the database. Understanding The Loop and custom queries is essential for displaying content anywhere on your site. The main query runs automatically on every page; custom queries let you display additional content like related posts, testimonials, or featured products.
Prerequisites
- Basic PHP understanding (variables, arrays, loops)
- Access to theme files or functions.php
- Understanding of WordPress posts/pages concept
Step-by-Step Guide
Understand the Main Query vs Custom Queries
The Main Query: Runs automatically on every page load based on the URL
On a blog page: retrieves latest posts. On a category page: retrieves posts in that category
Custom Query: A new WP_Query instance you create to display separate content
Use custom queries for: related posts, sidebars, testimonials, featured products
DO NOT use custom queries to modify the main content - use pre_get_posts instead
Learn When to Use Custom Queries
YES - Display related posts in a sidebar
YES - Show 3 recent testimonials on a homepage
YES - Display featured products in a widget
NO - Filter the main blog archive results (use pre_get_posts filter instead)
NO - Change the order of posts on category pages (use pre_get_posts)
The rule: If modifying what the page naturally shows, use pre_get_posts. If adding extra content, use WP_Query
Build Your First Custom Loop
Step 1 - Define Arguments: Specify what posts you want
Step 2 - Create Query: Instantiate WP_Query with arguments
Step 3 - Check for Posts: Use $the_query->have_posts()
Step 4 - Start The Loop: Use while ($the_query->have_posts()) : $the_query->the_post();
Step 5 - Display Data: Use template tags like the_title(), the_content()
Step 6 - End The Loop: Use endwhile;
Step 7 - Reset Post Data: CRITICAL - use wp_reset_postdata();
// 1. Define Arguments
$args = array(
'post_type' => 'product', // Query the 'product' CPT
'posts_per_page' => 4, // Limit to 4 posts
'orderby' => 'rand' // Get random products
);
// 2. Create Query
$the_query = new WP_Query( $args );
// 3. Check for Posts & 4. Start The Loop
if ( $the_query->have_posts() ) :
echo '<ul>';
while ( $the_query->have_posts() ) : $the_query->the_post();
// 5. Display Data
echo '<li><a href="' . get_permalink() . '">' . get_the_title() . '</a></li>';
// 6. End The Loop
endwhile;
echo '</ul>';
endif;
// 7. Reset Post Data (CRITICAL!)
wp_reset_postdata();Master Template Tags vs Global Functions
Template Tags echo output directly: the_title(), the_permalink(), the_content()
Global Functions return values for custom use: get_the_title(), get_permalink(), get_the_content()
Use echo functions when outputting directly in templates
Use get_ functions when you need to manipulate the value first
Example: the_title() outputs the title; get_the_title() returns it for storage/manipulation
Modify Main Query with pre_get_posts
Use pre_get_posts action hook to modify the main query before it runs
This runs before the database query - no extra database calls
Always check: is this the main query? is this the front-end (not admin)?
Add this code to your theme functions.php or a custom plugin
// Change posts per page on the blog archive
add_action( 'pre_get_posts', 'modify_main_query' );
function modify_main_query( $query ) {
// Safety checks: main query and front-end only
if ( $query->is_main_query() && ! is_admin() ) {
// Change posts per page on blog archive
if ( $query->is_home() ) {
$query->set( 'posts_per_page', 12 );
}
// Exclude a category from blog
if ( $query->is_home() ) {
$query->set( 'category__not_in', array( 5 ) );
}
}
}Verification Checklist
- Understand difference between Main Query and Custom Query
- Can write a custom WP_Query loop with proper arguments
- Always call wp_reset_postdata() after custom loops
- Know when to use WP_Query vs pre_get_posts
- Can use both template tags (echo) and global functions (return)
Pro Tips
- Use WP_Query with post__in to display specific posts by ID
- The meta_query parameter lets you filter by custom fields
- Use posts_per_page => -1 to get all posts (careful with large datasets)
- Check the WordPress Codex for all available WP_Query parameters
- pre_get_posts runs on admin queries too - always check !is_admin()