Why You Should Never Use Query_Posts() – And What To Use Instead

Why query_posts() is Dangerous for Your Site

The query_posts() function has been part of WordPress since the early days. This simple-to-use function allows you to modify the main query that displays posts on an archive or home page. However, query_posts() has some major downsides:

Overwrites the main query

One of the biggest issues with query_posts() is that it overwrites the original main query for the page. This means that any posts, pages, or custom post types that were going to be displayed get discarded and replaced. Using query_posts() destroys the carefully crafted main query that WordPress uses to display your blog posts, archive pages, search results etc.

Can cause unexpected issues

By replacing the default post query, query_posts() can introduce all sorts of unexpected problems:

  • Breaks pagination – Any pagination setup stops working properly once the main query is overwritten.
  • Creates conflicts with SEO and analytics plugins – Plugins that modify archive and search page output can be impacted since query_posts() breaks the normal page context.
  • Causes breadcrumb and comments issues – These features rely on the main query and can display incorrectly once it gets overwritten.
  • Problems with caches and object caches – Cached page output can become incorrect or stale once query_posts() is used.

Breaks pagination

One of the biggest side-effects of using query_posts() is breaking pagination. By replacing the main query, the careful pagination setup that WordPress uses gets destroyed, leaving any further page links non-functional. This leads to situations where a user clicks through to page 2,3,4 etc of a blog but just sees the same posts repeating over and over.

Debugging broken pagination caused by query_posts() can be tricky and time-consuming. For sites using explicit page links or a “load more posts” button, this type of pagination failure is especially serious. Users are left wondering why they see the same posts no matter how far they click through the pages.

Use WP_Query() or get_posts() Instead

So if you can’t use query_posts() to modify queries, what should you use instead? The two best alternatives are:

  • WP_Query()
  • get_posts()

These two functions allow safe, targeted custom queries that don’t impact the global $wp_query or overwrite the main query for a page.

More customizable queries

Both WP_Query() and get_posts() allow extremely customizable queries that only fetch the posts you need. You can query by category, tag, post type, date, custom field etc. or any combination of these.

By targeting just the subset of posts you want, you can efficiently populate custom page templates, widgets, shortcodes etc. without needing to impact global post listings or archive pages.

Doesn’t modify main query

These alternative query functions don’t touch the main $wp_query. This avoids all the issues around breaking pagination, breadcrumbs etc. that come with query_posts().

WP_Query() and get_posts() also don’t leave any traces once done. The global $post variable isn’t changed long-term so there are no side-effects that carry over.

Integrates with pagination properly

For queries needing to span multiple pages, both WP_Query and get_posts support full pagination integration. You can initialize the query object, point paginated links to the correct next page URL then rerun the query. Everything works seamlessly without needing messy offsets.

Example Usage of WP_Query() and get_posts()

Let’s look at some examples of how to use these alternative query functions…

Basic WP_Query example

Here is a simple WP_Query to get latest posts from a certain category:

$args = array(  
  'category_name' => 'news',
  'posts_per_page' => 10
);
 
$query = new WP_Query( $args );
 
if ( $query->have_posts() ) {
   while ( $query->have_posts() ) {
      $query->the_post();
      // Display post excerpt, title etc
   }  
   wp_reset_postdata();
}

Custom query arguments for WP_Query

Some other query arguments you can use with WP_Query():

$args = array(
  'tag' => 'football', //Fetch posts by a certain tag
  'post_type' => 'page', //Query a custom post type  
  'date_query' => array( //Get posts from certain date ranges  
    array(
        'year' => 2022,
    ),
 

Basic get_posts example

An equivalent get_posts() example:

$args = array(  
  'category_name' => 'news',
  'posts_per_page' => 10
);
 
$posts = get_posts( $args );
 
foreach ( $posts as $post ) {
    setup_postdata( $post );
    // Display post excerpt, title etc
}
wp_reset_postdata();

Differences in returned data between functions

While they can achieve similar queries, there are some major differences between WP_Query() and get_posts():

  • WP_Query returns a query “object” with added methods like pagination handling.
  • get_posts simply returns an array of post objects matching the query.
  • WP_Query allows easy access to grouped post data via properties like $query->posts and $query->post_count.
  • get_posts requires manual post looping and handling.

In most cases WP_Query() is the best choice due to the additional context it returns. But get_posts can be useful for basic needs or if you specifically want just an array of post data.

When query_posts Might Still Make Sense

Despite all the warnings, are there any cases where using query_posts might still be appropriate?

There are a handful of niche situations where it can work as an easy shortcut:

Some niche use cases where simplicity needed

If building a simple custom page template to display latest posts from a category, using query_posts() can be simpler than spinning up a new WP_Query object.

Just remember to call wp_reset_query() afterwards and don’t try adding pagination.

Using with caution in limited contexts

As long as it’s used with care in very narrowly defined contexts, query_posts can simplify displaying custom post listings without too many dangerous side effects:

  • On archive-like pages that don’t require pagination.
  • In custom page templates that reset afterwards with wp_reset_query()
  • To populate a simple custom widget with latest posts.

The key thing is restricting use of query_posts() to very isolated spots. Don’t use it on key site pages like blog listings. And take care to undo any query changes afterwards.

Leave a Reply

Your email address will not be published. Required fields are marked *