Create a Custom Post Type Filter by Taxonomy
In this tutorial, we’re going to create two different custom post type filters by custom taxonomy; the first one will be on site pages with posts (CPT archive pages):

The second one – in the WordPress admin, in the post list:

After that, we will also dive deep into the whole theory thing.
Example 1. Add Custom Post Type Filter by Taxonomy to CPT Archive Pages (Using a Plugin)
The main question here is – are you using a classic WordPress theme or a block theme? Because, depending on the answer, the approach is going to be completely different!
If you’re using a classic theme, I have at least two tutorials about creating a filter by taxonomy for a classic WordPress theme – one and two. So, why repeat myself over and over again? 🙃
Using a block theme, then, huh?
Obviously, you need some kind of taxonomy filter block. Sure thing, you can start developing it completely from scratch, but I think it is easily a topic not even of a completely different tutorial, but the whole course.
Or you can install a block as a WordPress plugin – Simple Taxonomy Filter Block.
No matter which way you choose, to create a post type filter for a WordPress block theme, you need a block. Once you have it, come back here and continue reading.
First of all, in WordPress admin, we need to navigate to Appearance > Editor > Templates. Now we need to open a template for our custom post type. “All archives” is not exactly what we need here, so, if you don’t have it, click on the “Add New Template” button and then select “Archive:{Name of CPT}”.

Can not find it in the list?
Make sure that when you registered this custom post type, you used the correct values of the following parameters:
register_post_type(
'restaurant',
array(
...
'show_in_rest' => true,
'has_archive' => true,
)
);The next thing you need to do is to navigate into the “Query Loop” block and add a “Taxonomy Filter” block to it.

After that, you can feel free to dive into the taxonomy filter block configuration, but the most important thing is to choose a custom taxonomy in the block settings.

On that – let’s hit the “Save” button and check the custom post type archive page. Yes, our filter by taxonomy works even without a page refresh.

Add taxonomy for a custom post type when registering it
This part is probably quite obvious; however, I think I should mention it here as well, in case you have some issues with your custom post type filter by a taxonomy, but you’re not sure what the root course is.
Long story short, you need to make sure that the taxonomies you’re about to use in filters are added to this specific post type.
How to do that?
Well, it depends on how you register taxonomies and post types on your WordPress site.
For example, if you’re using register_post_type() and register_taxonomy() functions, all you need to do is provide the second argument for the register_taxonomy() function like this:
register_taxonomy(
'my_taxonomy',
array(
'my_post_type', // you can provide multiple post type names here
),
$args
);If you’re using a plugin for registering custom taxonomies, then it all depends on the plugin you’re using.
For example, in the CPT UI plugin, you need to select the appropriate checkboxes when adding a taxonomy:

Example 2. Add Post Type Filters by Taxonomy to the Posts List in WordPress Admin
I’m going to start with a quick hint here – if, when registering a custom taxonomy, you will provide a parameter show_admin_column set to true, then you’ve already going to have some decent filters – all you need to do to filter posts is to click on a specific taxonomy term link in an admin column:

A more thorough solution is to add one more filter above the post list:

You can easily do it with the code snippet below:
add_action( 'restrict_manage_posts', function(){
// we can use $typenow parameter to identify the current page
global $typenow;
// check the post type here
if( 'restaurant' !== $typenow ) {
return;
}
$taxonomy_name = 'city';
$selected = isset( $_GET[ $taxonomy_name ] ) ? $_GET[ $taxonomy_name ] : false;
// basically, we need it only do display a label
$taxonomy = get_taxonomy( $taxonomy_name );
wp_dropdown_categories( array(
'show_option_all' => 'Show all ' . strtolower( $taxonomy->label ),
'taxonomy' => $taxonomy_name,
'name' => $taxonomy_name,
'selected' => $selected,
'hierarchical' => true,
'value_field' => 'slug',
'depth' => 3,
'show_count' => true,
'hide_empty' => true,
) );
} );As you can see, we don’t even need to tap into the pre_get_posts hook, because the filtering is already working great when your URL contains the taxonomy slug.
Filtering Custom Post Types by a Taxonomy in WP_Query
A tiny bit of theory first, all right?
Always, I’d like to repeat it – always, when we create any kind of post filters by taxonomies, we’re using the tax_query parameter of WP_Query (get_posts(), query_posts(), etc).
Even in my taxonomy filter block for Gutenberg, I’m also using it; I mean, I don’t interact with the REST API directly.
The usage of the tax_query parameter is pretty straightforward – it allows you to specify one or multiple taxonomies you’d like to filter your posts by and configure the conditional relationships between them. I am about to show you everything in the examples below.
Example of using tax_query
Here you can see a basic example of using the tax_query argument:
$args = array(
'post_type' => 'restaurant',
'tax_query' => array(
// taxonomy 1
array(
'taxonomy' => 'city',
'field' => 'slug', // by default, you need to pass term IDs
'terms' => array(
// you can pass multiple terms in an array
'athens',
),
),
// taxonomy 2
// array(
// ...
// ),
),
);
$query = new WP_Query( $args );Long story short:
- the
post_typeparameter is needed because we get only posts of a specific type, - the
fieldparameter must be set to theslugvalue, otherwise we will need to provide IDs of taxonomy terms instead of slugs, for example, 123 instead of “athens”, - you can add as many taxonomy conditions as you want.
Filtering by multiple taxonomy terms at the same time
There are two scenarios here:
- when you’d like to filter by different terms of a single taxonomy,
- when you’d like to filter by different terms of different taxonomies.
Let’s take a look at both of them.
Filtering custom post types by multiple terms of a single taxonomy:
$args = array(
'post_type' => 'restaurant',
'tax_query' => array(
// taxonomy 1
array(
'taxonomy' => 'city',
'field' => 'slug',
'operator' => 'IN', // default
'terms' => array( 'athens', 'belgrade', 'berlin' ),
),
),
);
$query = new WP_Query( $args );Then the most important parameter for you is – operator. It can accept the following values:
IN(default) – posts should have at least one of the mentioned taxonomy terms,AND– posts should have all the taxonomy terms at the same time,NOT IN– should not have any of the mentioned terms.EXISTS– when posts have any of the terms of the mentioned taxonomy, in that case, we don’t needtermsandfieldparameters.NOT EXISTS– kind of similar to the previous parameter, but with the opposite effect.
Filtering custom post types by multiple terms of different taxonomies:
$args = array(
'post_type' => 'restaurant',
'tax_query' => array(
'relation' => 'AND', // Default
// taxonomy 1
array(
'taxonomy' => 'city',
'field' => 'slug',
'terms' => array( 'barcelona', 'kuala-lumpur' ),
),
// taxonomy 2
array(
'taxonomy' => 'dish',
'field' => 'slug',
'terms' => array( 'paella' ),
),
),
);
$query = new WP_Query( $args );In the example above, we’re trying to display all restaurants (our custom post type) from specific cities (Barcelona, Kuala Lumpur) that serve a specific dish (paella).
Take a look at the relation parameter – it is a relation between the taxonomies:
AND(default) – all taxonomy rules should be applied,OR– any of the rules is more than enough.
The cool thing is that you can add as many inner conditions as you want:
$args = array(
'post_type' => 'restaurant',
'tax_query' => array(
'relation' => 'OR',
array(
'taxonomy' => 'city',
'field' => 'slug',
'terms' => array( 'barcelona', 'madrid' ),
),
array(
'relation' => 'AND',
array(
'taxonomy' => 'city',
'field' => 'slug',
'terms' => array( 'kuala-lumpur', 'dubai' ),
),
array(
'taxonomy' => 'dish',
'field' => 'slug',
'terms' => array( 'paella' ),
),
),
),
);
$query = new WP_Query( $args );Guys, if you have any questions, please let me know in the comments below.
Misha Rudrastyh
Hey guys and welcome to my website. For more than 15 years I've been doing my best to share with you some superb WordPress guides and tips for free.
Need some developer help? Contact me
Hi Misha,
Very nice tutorial!
Do you know how to setup a “default value” for such filter?
In your example, it could be “Athens” as default value, rather than “All cities”.
Thanks! :)
Hey Romain,
You didn’t clarify, which example you’re referring to 🙃
For example, in the second example, you can pass it into the
$selectedvariable (instead offalse).