How to Filter Posts by Category in WordPress

In this guide, I will dive deep into the process of creating an AJAX category filter for a WordPress website. We will do it both with the help of plugins and without plugins at all.

Method 1. AJAX Category Filter – WordPress Plugin (Recommended)

Let’s start with the plugin approach. I would say that it is a recommended method, because nowadays the block themes in WordPress prevail, and if you need to filter posts by category the proper way you can not just edit your theme template files like index.php or something; instead, you need to create an interactive Gutenberg block which is a child block for the “Query Loop” block and it should interact with the query loop similar to how the “Pagination” block does. Under an “interactive block,” I mean a block that uses the Interactivity API.

However, developing an interactive Gutenberg block from scratch is not an easy task; you need to learn at least the basics of React and something about creating Gutenberg blocks.

So, here are the two plugins you can use for that goal:

  • Category Filter Block (Free) – allows you to create AJAX category filters with some basic features.
  • Simple Taxonomy Filter Block (Premium) – a more advanced plugin that allows you not only to work with custom taxonomies but also comes with a whole bunch of customization options.

Below is an example of an AJAX post filter by category created with my plugin for a website with the default WordPress block theme installed – Twenty Twenty-Four:

Filter posts by category in a WordPress block theme
As you can see, the filter works instantly – thanks to the WordPress Interactivity API.

If you decide to use the premium version of the plugin, then you can do much more; for example, you can use a multiselect with checkboxes in your filter the following way:

Using an AJAX category filter WordPress plugin

All you need to do to allow this multi-selection with checkboxes is use a single block setting:

WordPress category filter plugin block settings

This AJAX category filter plugin for WordPress also comes with a bunch of other useful features, for example:

  • You can not only filter posts by category but also filter these posts in the same query loop by tags or any other custom taxonomy at the same time (in other words, you can add multiple filters into the same loop).
  • Filters can also be displayed as a list of links or as buttons with fully customizable styles.
  • You can hide or show post counts.
  • You can sort the categories (or terms) in your filter in the desired order.

And so on…

If I got your attention, you can check the Simple Taxonomy Filter Block plugin yourself; however, if you’re undecided, here is a demo page with more examples.

Method 2. Filter Posts by Category in WordPress without Plugins

In the example below, we’re going to use the following:

  • Pure JavaScript on the front end. I know, many of you guys don’t like jQuery, and since it is not a filter for a WooCommerce store and the library isn’t included in the front-end anyway, there is no real reason to use it.
  • A default “Twenty Twenty” WordPress theme. We can not use the latest themes like Twenty Twenty-Four or Twenty Twenty-Five, because the latest themes are block themes, and in that case, we will need to create a Gutenberg block for our AJAX category filter, which is a completely different story. Currently, I don’t have a tutorial for that, but if you need a category filter for a WordPress block theme, please check the first part of the tutorial.

Below is the preview of how our AJAX category filter is going to look:

Filter posts by category in WordPress without plugins

Now, let’s break down the process of creating this AJAX category filter step by step.

Display a Category Filter as a Select Dropdown in HTML

Our first goal when we need to filter WordPress posts by category without a plugin is to create some HTML for the filter and then to decide where to insert it into the WordPress theme that we’re about to use (in our case – “Twenty Twenty”).

<div class="ajax-filters">
	<form id="ajax-filter">
		<?php
			$categories = get_terms( // you can use get_categories() function as well
				array(
					// you can replace the taxonomy parameter value with any custom taxonomy name or 'post_tag'
					'taxonomy' => 'category',
					'orderby' => 'name',
				) 
			);
			if( $categories ) :
				?>
					<select>
						<option value="">Select category...</option>
						<?php
							foreach ( $categories as $category ) :
								?><option value="<?php echo $category->term_id ?>"><?php echo $category->name ?></option><?php
							endforeach;
						?>
					</select>
				<?php
			endif;
		?>
	</form>
</div>

The question is – where to use this code? Well, it depends on your theme, of course. In “Twenty Twenty” it is better to create a child theme and use it at the very beginning of index.php file.

By the way, it is also possible to combine this code with my multisite queries plugin and its network_get_terms() function. So, all the network categories will be in the select dropdown, and all the network posts will be displayed as the filtered search results.

Send Asynchronous Requests with JavaScript

Now it is time for what? To do some JavaScript stuff!

We actually need to create a form submit event for our filter, which is going to send asynchronous requests to the server, to WordPress’s admin-ajax.php file, and it is actually where we’re going to return filtered posts. But since a select dropdown is the only field in our filter for now, we just need an event when it is changed.

const ajaxFilter = document.getElementById( 'ajax-filter' )
const siteContent = document.getElementById( 'site-content' )

ajaxFilter.querySelector( 'select' ).addEventListener( 'change', event => {
	
	// .is-loading{ opacity: 0.5 } creates that opacity-like effect
	siteContent.classList.add( 'is-loading' )
	
	fetch( ajaxurl + '?action=ajaxfilter', {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json'
		},
		body: JSON.stringify( {
			// filter WordPress posts by category
			'cat' : event.target.value 
		} ),
	}).then( response => {
		return response.text()
	}).then( response => {

		if( response ) {
			siteContent.innerHTML = response;
		}
		siteContent.classList.remove( 'is-loading' )
		// console.log( response );

	}).catch( error => {
		console.log( error )
	})

} )

If you need to filter posts by category (without a plugin – just a reminder) using the jQuery library, you can check my other tutorial where I am doing it for WooCommerce.

In the code above, we also have an undefined variable, which is ajaxurl. Here I assume that its value is the default one in WordPress – /wp-admin/admin-ajax.php, but you can pass the whole value as well, like add_query_arg( 'action', .... Well, I think a little bit of an example won’t hurt.

add_action( 'wp_enqueue_scripts', function() {
	
	wp_register_script( 'mishafilter', 'URL HERE', array(), time(), true );
	wp_localize_script( 
		'mishafilter',
		'misha_args',
		array(
			'ajaxurl' => add_query_arg( 
				array( 
					'action' => 'ajaxfilter' 
				),
				admin_url( 'admin-ajax.php' )
			)
		)
	)
	wp_enqueue_script( 'mishafilter' );
	
} );

In that case, we will need to change one line in the JavaScript code above:

fetch( misha_args.ajaxurl, {

Get Posts Filtered by Category inside wp_ajax_ Hook

Last but not least – the part which goes into the functions.php file of your child theme. Here we’re going to get the filtered posts HTML and return it as a result of our AJAX request.

add_action( 'wp_ajax_ajaxfilter', 'rudr_ajax_filter_by_category' );
add_action( 'wp_ajax_nopriv_ajaxfilter', 'rudr_ajax_filter_by_category' );

function rudr_ajax_filter_by_category() {

	$args = json_decode( file_get_contents( "php://input" ), true );

	query_posts( $args );
	
	// below is almost unchanged part from Twenty Twenty theme index.php file
	$i = 0;

	while( have_posts() ) {
		++$i;
		if ( $i > 1 ) {
			echo '<hr class="post-separator styled-separator is-style-wide" />';
		}
		the_post();

		get_template_part( 'template-parts/content', get_post_type() );

	}

	die;

}

This is not a very complicated code, but just in case, I would like to remind you about:

  • When we send AJAX requests with jQuery.ajax, we can access the data from $_POST global variable, but not when we send requests with fetch(). That’s why using the line with file_get_contents( "php://input" ) is essential here.
  • Also, in this case, I prefer to use query_posts() instead of WP_Query here, because, anyway, we exit the code with die at the end, so there is no need to worry about global variables, but it may save the day when loading templates with get_template_part() in some cases.
  • And yes, I hope you know how wp_ajax_ and wp_ajax_nopriv_ actions work – it is just a standard way of handling AJAX requests by WordPress.

If you have any questions about the code or about the plugin, please ask in the comments.

Misha Rudrastyh

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

Follow me on X