WP Tax Query Generator

Build a WordPress tax_query array: taxonomies, term fields, operators, child term handling and the relation between clauses, ready for WP_Query.

Live output

Enable JavaScript to customise; default output below.

Output

tax_query only gives you the array on its own, for pasting into an existing query.

-1 means every matching post, which is rarely what an archive needs.

Live preview tax-query.php
<?php
/**
 * A tax_query over post.
 */

$args = array(
	'post_type'      => 'post',
	'posts_per_page' => 10,
	'tax_query'      => array(
		array(
			'taxonomy' => 'category',
			'field'    => 'slug',
			'terms'    => array_map( 'trim', explode( ',', 'news, updates' ) ),
			'operator' => 'IN',
			'include_children' => true,
		),
	),
);

$query = new WP_Query( $args );

if ( $query->have_posts() ) {
	while ( $query->have_posts() ) {
		$query->the_post();

		the_title( '<h2>', '</h2>' );
	}

	// Always restore the global post after a custom loop.
	wp_reset_postdata();
}

Output is valid and updates as you type.

Pick the taxonomies, how the terms are matched and what to do with child terms, and the generator writes the tax_query array with the relation and operators already in place.

How to use

  1. Add one clause per taxonomy. Two clauses with AND is the normal way to ask for posts in a category and with a tag.
  2. Match by slug unless you have the IDs in hand. Slugs survive an export and an import; term IDs do not.
  3. Type the terms separated by commas. The generated code splits them, and casts them to integers when you match by ID.
  4. Turn off child terms when a parent should mean only itself. It is on by default in WordPress, which surprises people with hierarchical categories.
  5. Use AND as the operator when a post must carry every term, not just one of them.

Example

Posts in the news category, excluding anything tagged sponsored, with child categories left out:

$args = array(
	'post_type'      => 'post',
	'posts_per_page' => 10,
	'tax_query'      => array(
		'relation' => 'AND',
		array(
			'taxonomy' => 'category',
			'field'    => 'slug',
			'terms'    => array_map( 'trim', explode( ',', 'news' ) ),
			'operator' => 'IN',
			// Off, so a parent term does not drag its children in.
			'include_children' => false,
		),
		array(
			'taxonomy' => 'post_tag',
			'field'    => 'slug',
			'terms'    => array_map( 'trim', explode( ',', 'sponsored' ) ),
			'operator' => 'NOT IN',
			'include_children' => true,
		),
	),
);

Note the difference between the two operators: IN is any of these terms, AND is all of them.

Pitfalls

  • include_children defaults to true. A query for a parent category quietly returns everything underneath it.
  • Term IDs are not portable. A staging site and a live site rarely agree on them, which is why slug is the safer field.
  • IN means any of the terms. Asking for posts carrying every term needs the AND operator, which is a different thing from the AND relation.
  • NOT IN with include_children on also excludes the children. That is usually what you want and occasionally a surprise.
  • field => name matches the display name, which editors rename. It breaks the day someone fixes a typo in a term.
  • Each clause joins the term relationship tables again. Five clauses on a large site is a slow query, cached or not.
  • term_taxonomy_id does not need a taxonomy in practice, but leaving the taxonomy out makes the query harder to read and to change later.
  • Mixing tax_query with the category_name or tag shorthands in the same query gives you two sets of conditions and confusing results. Pick one.

Compatibility

tax_query has been in WP_Query since WordPress 3.1, and nested clauses with their own relation since 4.1. The EXISTS and NOT EXISTS operators need WordPress 4.5 or later. The generated code targets PHP 7.0 and up, and the tool runs entirely in your browser.

Frequently asked questions

Why am I getting posts from child categories?
include_children is true unless you say otherwise. Set it to false for a parent term that should mean only itself.
Slug or term ID?
Slug, unless the IDs come from the same database you are querying. IDs change between environments; slugs travel.
What is the difference between the AND relation and the AND operator?
The relation joins clauses together. The operator, inside one clause, asks for posts carrying every term in that clause.
Can I combine this with a meta query?
Yes, both arrays sit in the same $args. They are joined with AND, and each adds its own joins, so keep an eye on the cost.
How do I query for posts with no terms at all?
Use the NOT EXISTS operator with just the taxonomy. It needs no terms and no field.

From the people who built this tool

WP Adminify

The WordPress admin, rebuilt: a dashboard worth looking at, menu and column control, a real file manager and the login page your client sees.

See WP Adminify Free version on WordPress.org

Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.