It seems like half the tutorials in the Codex and around the blogosphere use query_posts() and half use WP_Query.

They all do similar things, so when should I use one over the others?


7 Answers 7

  • query_posts() is overly simplistic and a problematic way to modify the main query of a page by replacing it with new instance of the query. It is inefficient (re-runs SQL queries) and will outright fail in some circumstances (especially often when dealing with posts pagination). Any modern WP code should use more reliable methods, like making use of the pre_get_posts hook, for this purpose. TL;DR don't use query_posts() ever.

  • get_posts() is very similar in usage and accepts the same arguments (with some nuances, like different defaults), but returns an array of posts, doesn't modify global variables and is safe to use anywhere.

  • WP_Query is the class that powers both behind the scenes, but you can also create and work with your own instance of it. A bit more complex, fewer restrictions, also safe to use anywhere.

WP_Query vs  query_posts vs get_posts diagram Image source: https://www.rarst.net/images/query_functions.png

  • 10
    @jjeaton query_posts() is tiny wrapper function for WP_Query, the only extra thing it does (as per flowchart) is overwriting global $wp_query
    – Rarst
    Commented Aug 8, 2011 at 15:39
  • 9
    @jjeaton Replacing query_posts() with WP_Query will make no difference in performance, original page's query will still run because that is part of core load. Those queries will run even if your template file has no loop at all.
    – Rarst
    Commented Aug 8, 2011 at 17:15
  • 127
    Can't get rid off the feeling that this the most genious and upvoted post on WPSE. Should be in Codex as well.
    – kaiser
    Commented Sep 16, 2011 at 0:03
  • 9
    I'll just add my clearest description of the "performance of query_posts()" issue: Using query_posts() or WP_Query within a template file will have the same performnace cost: the query you just performed. The issue discussed in the codex article is that if you actually want to replace the query you should do so by filtering the original query_posts() with the 'parse_query' filter. That way you only have the one, original, desirable query, rather than doing a second query to awkwardly replace it. query_posts() is NEVER THE WAY!! NEVER!
    – jerclarke
    Commented Apr 19, 2012 at 22:24
  • 23
    There's a freaking awesome explanation of query_posts written by John James Jacoby on the developer.wordpress.com blog that blows all of these answers out of the water. The main point: query_posts doesn't modify the main loop at all, it replaces it after it has already run. The best way to modify the main loop is through a pre_get_posts filter. developer.wordpress.com/2012/05/14/…
    – Dan Gayle
    Commented Jun 9, 2012 at 23:10

query_posts - You should never ever use query_posts. Apart from what @Rarst has said, the really big issue with query_posts is, it breaks the main query object ( stored in $wp_query ). A lot of plugins and custom code relies on the main query object, so breaking the main query object means that you are breaking the functionalities of plugins and custom code. Just one such function is the all important pagination function, so if you break the main query, you break pagination.

To prove how bad query_posts is, on any template, do the following and compare the results

var_dump( $wp_query );
query_posts( '&posts_per_page=-1' );
var_dump( $wp_query );

get_posts and WP_Query are the correct way to construct secondary queries ( like related posts, sliders, featured content and content on static front pages ) with. It should be noted, you should not use any of the two in favor of the main query on the home page, single page or any type of archive page as it will break page functionality. If you need to modify the main query, use pre_get_posts to do so, and not a custom query. (UPDATE: For static front pages and true pages, see Using pre_get_posts on true pages and static front pages*)

In essence, WP_Query is used by the main query and is also used by get_posts, but although get_posts() uses WP_Query, there are a few differences

  • get_posts are faster than WP_Query. The margin depends on the amount of total posts of the site. The reason for this is, get_posts passes 'no_found_rows' => true by default to WP_Query which skips/legally breaks pagination. With 'no_found_rows' => true, WP_Query gets the amount of posts queried, then bails out, where by default, it further search for all posts matching the query in order to calculate pagination.

    For this reason, get_posts() should be used for non paginated queries only. Paginating get_posts is really one big mess. WP_Query should be used for all paginated queries

  • get_posts() aren't influenced by the posts_* filters where WP_Query gets influenced by these filters. The reason is that get_posts, by default, passes 'suppress_filters' => true to WP_Query

  • get_posts has a couple of extra parameters like include, exclude, numberposts and category. These parameters do get changed into valid parameters for WP_Query before being passed to WP_Query. include gets changed into post__in, exclude into post__not_in, category into cat and numberposts into posts_per_page. Just a note, all of the parameters that can be passed to WP_Query works with get_posts, you can ignore and not use the default parameters of get_posts

  • get_posts returns just the $posts property of WP_Query while WP_Query returns the complete object. This object is quite useful when it comes to conditionals, pagination and other useful info that can be used inside the loop.

  • get_posts doesn't use the loop, but a foreach loop to display posts. Also, no template tags are available by default. setup_postdata( $post ) has to be used to make the template tags available. WP_Query uses the loop and template tags are available by default

  • get_posts passes 'ignore_sticky_posts' => 1 to WP_Query, so get_posts by default ignores sticky posts

Based on the above, whether to use get_posts or WP_Query is up to you and what do you actually need from the query. The above should guide you in your choice

  • 2
    I wish I could favorite answers. This explains so much. Commented Jun 12, 2017 at 7:17
  • 2
    Great Explanation! "get_posts() should be used for non paginated queries only. Paginating get_posts is really one big mess. WP_Query should be used for all paginated queries" Is basically all someone needs to know imo.
    – Bullyen
    Commented Apr 19, 2018 at 19:27
  • @pieter-goosen, do you know if this same is true for WP User Query versus get_users? Referring to the performance part.
    – user75665
    Commented Aug 5, 2021 at 3:34

The basic difference is that query_posts() is really only for modifying the current Loop. Once you're done it's necessary to reset the loop and send it on its merry way. This method is also a little easier to understand, simply because your "query" is basically a URL string that you pass to the function, like so:


On the other hand, WP_Query is more of a general purpose tool, and is more like directly writing MySQL queries than query_posts() is. You can also use it anywhere (not just in the Loop) and it doesn't interfere with any currently running post queries.

I tend to use WP_Query more often, as it happens. Really, it's going to come down to your specific case.


There is simply no need to use query_posts(). All it does is instantiates a new WP_Query object and reassigns that new object to global wp_query.

For reference, the following is that actual query_posts() function.

 function query_posts($query) {
        $GLOBALS['wp_query'] = new WP_Query();
        return $GLOBALS['wp_query']->query($query);

Instantiate your own WP_Query object if you want to create an in depth custom query script. Or use get_posts() if all you need to do is some light manipulation here and there.

In either case, I highly recommend doing yourself a favor and going to wp_includes/query.php and perusing the WP_Query class.


Make sure that you use wp_reset_query() after using query_posts() because it will affect other query result also.


If I recall reading right, essentially "the loop" is doing WP_Query in the core files, but in an easier to understand way.

  • query_posts(): might be used in one and only case if you need to modify main query . It sets a lot of global variables;
  • get_posts(): it is very similar in mechanics and accepts same arguments, but returns array of posts
  • WP_Query:  you can create and work with own object of it. Bit more complex, less restrictions, it is safe to use anywhere.

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