Combine Category and Tag Conditions in WP_Query

Quick Summary

  • Put a category condition and a post_tag condition in one tax_query with relation AND to show only posts matching both.
  • Each condition names the taxonomy, the field such as slug and a terms array; switching relation to OR matches either one.
  • With posts_per_page set to -1 all matches load and paged does nothing, so set a positive number for pagination.

A tax_query in WP_Query can hold several conditions, and its relation key decides how they are joined. The example below shows WordPress posts that are in the category new and have the tag special. A post that has only one of the two is left out.

Use the code in the template file where the list should appear, such as a page template or home.php.

PHP
<?php 
  $paged = get_query_var( "paged" )?get_query_var( "paged" ) : 1;
  $posts_per_page = -1;
  $_p = new WP_Query(array(
  	'posts_per_page'	=> $posts_per_page,
  	'paged'				=> $paged,
  	'tax_query'			=> array(
  
  		'relation' => 'AND',
  		array(
  			'taxonomy' => 'category',
  			'field'	   => 'slug',
  			'terms'	   => array('new')
  		),
  
  		array(
  			'taxonomy' => 'post_tag',
  			'field'	   => 'slug',
  			'terms'	   => array('special')
  		)
  
  	)
  ));
  
  
  while( $_p->have_posts() ):
  $_p->the_post();
  ?>


//POST TITLE
<h3><a href="<?php the_permalink(  ); ?>">
  <?php the_title(  ); ?>
  </a>
</h3>



<?php
  endwhile;
  wp_reset_query();
?>

How it works

  • 'relation' => 'AND' requires every condition in the array to match. Change it to 'OR' to show posts that match either one.
  • Each inner array is one condition: the taxonomy (category or post_tag), the field you match on (slug), and the terms. You can also match by term_id or name.
  • terms is an array, so one condition can list several slugs; by default a post matches if it has any of them.
  • posts_per_page is -1, which returns all matching posts at once, so the paged value has no effect. Set a positive number if you want pagination.

The loop prints each post’s title linked to the post. The //POST TITLE line is outside PHP and prints as text, so remove it. After a custom WP_Query loop, wp_reset_postdata() is the usual reset; wp_reset_query() is only needed after query_posts().

Share on X (opens in a new tab) Share on LinkedIn (opens in a new tab) Share on Facebook (opens in a new tab)

ChatGPT (opens in a new tab) Claude (opens in a new tab) Gemini (opens in a new tab) Perplexity (opens in a new tab) Grok (opens in a new tab)

More Insights

See More