WP_Query Builder
Pick the posts you want; get the query, the loop and a plain-English summary — plus warnings for the arguments that quietly kill performance.
// Generated with WP CodeKit — powered by GrowQuest (https://growquest.io). $args = array( 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 10, 'paged' => get_query_var( 'paged' ) ? absint( get_query_var( 'paged' ) ) : 1, 'orderby' => 'date', 'order' => 'DESC', ); $query = new WP_Query( $args ); if ( $query->have_posts() ) { echo '<ul class="mytheme-list">'; while ( $query->have_posts() ) { $query->the_post(); printf( '<li><a href="%s">%s</a></li>', esc_url( get_permalink() ), esc_html( get_the_title() ) ); } echo '</ul>'; wp_reset_postdata(); } else { echo '<p>' . esc_html__( 'Nothing found.', 'textdomain' ) . '</p>'; }
About this generator
WP_Query Generator Online
Build a WP_Query argument array from a form and get the query, the loop and the wp_reset_postdata() call back as one block of PHP. This wp_query generator covers post types and statuses, ordering, taxonomy and meta clauses, date bounds, author and ID filters, and the cache flags that decide how expensive the query actually is.
A Summary tab restates the query in plain English, lists every argument it will emit, and scores the result on a cost meter from Light to Very heavy so you can see a second JOIN or an unbounded posts_per_page land before you ship it. Free, no account, and nothing you type leaves the browser.
Reviewed Output tested on WordPress 6.8 · PHP 8.3
Why this WP_Query builder beats copying an args array off a forum post
The arguments are easy. What costs time is everything the array does not say out loud: that posts_per_page => -1 will load the whole table, that paged and no_found_rows cannot both be on, that a > comparison against a CHAR cast makes MySQL decide "9" is larger than "10". This builder writes the array and audits it against those exact failures.
A cost meter, not a vibe
Every clause, an unbounded page size, orderby => rand and a search term each add to a score that resolves to Light, Moderate, Heavy or Very heavy, with a one-line note telling you whether to cache it or reach for Query Monitor.
The pagination contradiction is an error
Turning paged on while no_found_rows is set is flagged as an error, not a warning, with a one-click fix — pagination needs the found-rows count, and a query missing it silently renders a single page.
It offers no_found_rows when you should have it
A query with no pagination and no offset gets a tip to set no_found_rows, which drops the extra SQL_CALC_FOUND_ROWS pass MySQL otherwise runs on every request. One click applies it.
Meta casts checked against the comparison
A clause comparing with >, >=, <, <=, BETWEEN or NOT BETWEEN while cast to CHAR is called out by clause number, because text ordering puts "9" above "10". Ordering by meta_value instead of meta_value_num gets the same treatment.
Three output shapes from one form
The same arguments render as a secondary query with its loop, a pre_get_posts callback that adjusts the main query instead of running a second one, or a registered shortcode. Set fields => ids and the loop changes to a plain foreach over $query->posts — no post objects hydrated.
Plain English before PHP
The Summary tab writes the query as a sentence — how many of which post type, filtered by which taxonomy and meta, ordered how, paginated or not — so you can check the intent without reading the array.
How does the WP_Query generator work?
The form runs top to bottom in the order the query is assembled: what to fetch, how to filter it, then the advanced arguments most queries never need.
- 01
Choose what to fetch
Pick built-in post types or add your own slug, choose the statuses, set
posts_per_page, decide whether the query paginates, and setorderbyandorder. Ordering by a meta value reveals the meta key field. - 02
Add taxonomy and meta clauses
Each taxonomy clause takes a taxonomy, a field (
slug,term_id,nameorterm_taxonomy_id), an operator and a comma-separated term list. Each meta clause takes a key, a comparison, a value and a cast. Set the relation toANDorORonce both lists have more than one entry. - 03
Open the advanced sections if you need them
Authors, dates, search,
post__inandpost__not_inlive under one collapsible section;fields,no_found_rows, the meta and term cache flags andsuppress_filterslive under the other. - 04
Clear the Checks tab, then export
Apply the one-click fixes, choose the loop,
pre_get_postsor shortcode output, then copy the snippet or download the.phpfile.
Worked example — six latest posts, no pagination, no count query
A sidebar list. Because it never paginates, no_found_rows is on, which removes the row-counting pass MySQL would otherwise run alongside the main query.
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => 6,
'orderby' => 'date',
'order' => 'DESC',
'no_found_rows' => true,
);
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
echo '<ul class="mytheme-list">';
while ( $query->have_posts() ) {
$query->the_post();
printf(
'<li><a href="%s">%s</a></li>',
esc_url( get_permalink() ),
esc_html( get_the_title() )
);
}
echo '</ul>';
wp_reset_postdata();
} else {
echo '<p>' . esc_html__( 'Nothing found.', 'textdomain' ) . '</p>';
}wp_reset_postdata() is emitted for you. Without it the global $post is left pointing at the last row of this query, and every template tag after the loop — including the ones in your footer — reports the wrong post.
WP_Query — frequently asked questions
The questions that come up every time someone writes a custom loop.
What is the difference between WP_Query, get_posts() and query_posts()?
WP_Query is the class; you instantiate it, loop it, then call wp_reset_postdata(). get_posts() wraps it and returns a plain array of post objects, but changes some defaults — notably it sets suppress_filters to true and no_found_rows to true, so plugin filters and pagination counts are skipped. query_posts() replaces the main query globally and should never be used; it breaks pagination and conditional tags on the page it runs in. Use pre_get_posts when you mean to change the main query.
Why should I use pre_get_posts instead of a second WP_Query on an archive page?
On an archive, WordPress has already run the main query before your template loads. Adding a second WP_Query means the database does the work twice and the page still paginates against the query you ignored, so page 2 shows the wrong posts. Hooking pre_get_posts and calling $query->set() modifies the query WordPress was going to run anyway. Always guard the callback with if ( is_admin() || ! $query->is_main_query() ) { return; } or you will change every query on the site, including the admin post list.
Is posts_per_page => -1 bad?
It is unbounded, which is the problem. -1 removes the LIMIT clause, so the query returns every matching row and PHP hydrates a post object for each one. That is fine against fifty posts and fatal against fifty thousand, and the failure only appears once real content exists. If you genuinely need everything, set fields => ids so no post objects are built, and cache the result.
How do I paginate a custom WP_Query?
Pass 'paged' => get_query_var( 'paged' ) ? absint( get_query_var( 'paged' ) ) : 1 in the arguments, then render links with paginate_links() using $query->max_num_pages as the total. no_found_rows must stay off, because max_num_pages is derived from the found-rows count that flag removes. On a static front page the query var is page rather than paged.
What does no_found_rows actually do?
It tells WordPress to skip counting how many rows matched in total. Normally the query runs with SQL_CALC_FOUND_ROWS and then issues a SELECT FOUND_ROWS() to populate $query->found_posts and max_num_pages. If nothing on the page shows a total or a pager, that work is wasted, and on a large table the counting pass can cost more than fetching the rows.
Do I need wp_reset_postdata() after every WP_Query?
After any loop that called $query->the_post(), yes. the_post() overwrites the global $post, so template tags such as get_the_title() or comments_template() used later in the page report data from your secondary loop instead of the real one. If you never call the_post() — for example when you iterate $query->posts with fields => ids — there is nothing to reset.
Where do I paste the code from the WP_Query generator?
You have three good options and one bad one. Best is a small site-specific plugin — the generator can output one for you, and it keeps working when you change theme. A code-snippets plugin is fine too. A child theme's functions.php works but ties the code to that theme. Never edit the parent theme: the next update overwrites it and your code is gone.
Can I use the generated code in client work or a commercial plugin?
Yes. The output is ordinary WordPress API calls that you configured — there is no licence attached to it and no attribution required. Use it in client sites, commercial plugins and themes, or products you sell. The generated file carries a short credit comment you are free to delete.
The other Query generators in the library, plus the WordPress tools most often used alongside this one.