FacetWP works well with custom WP queries, as long as you point FacetWP to that query with a special query argument, as shown below.

How to create a custom query

For FacetWP to work with the custom query, you need to add facetwp => true to the query arguments. FacetWP uses this special argument to automatically detect this query as the main query to use for filtering. Without it, facets will not show up.

How to use custom PHP code?

PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info

$args = [ "post_type" => ["event"], "posts_per_page" => 10, "orderby" => ["title" => "ASC"], // ... your arguments "facetwp" => true // this flags this custom query to be used by FacetWP ]; $my_query = new WP_Query( $args );

A basic loop that uses this custom query would look something like this:

How to use custom PHP code?

PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info

if ( $my_query->have_posts() ) : while ( $my_query->have_posts() ) : $my_query->the_post(); ?> <h1><?php the_title(); ?></h1> <div><?php the_content(); ?></div> <?php endwhile; else : _e( 'Sorry, no posts matched your criteria.' ); endif; wp_reset_postdata();

Solving issues with FacetWP’s automatic post loop detection

Most of the time, after placing the custom query and some facets on a single page/post (not on an archive), the above template will just work: FacetWP will automatically detect the custom query and the post loop that it needs to dynamically change when filtering with facets.

However, there are circumstances in which FacetWP’s automatic detection fails, resulting in the facets and listing not being updated.

How it works

The above facetwp => true query arguments flags the custom query as the main query to use for filtering.

Using WP’s loop_start hook, Facetwp identifies the post loop that uses this query, and a <!--fwp-loop--> HTML comment is placed above the post loop in the template.

Next, FacetWP’s front-end JavaScript looks for that HTML comment and automatically adds a facetwp-template class on its direct parent HTML element, unless there is already another facetwp-template class present on the page. FacetWP needs this class to dynamically replace all HTML within this container with the AJAX refresh that happens when facets are used.

When the facetwp-template class ends up missing because one of these steps go wrong, the solution is add it manually.

Note that this is also the fix for issues with some caching and optimization plugins (like Cloudflare) that minify HTML. HTML minification removes HTML comments, including the <!--fwp-loop--> comment needed by FacetWP.

Fix the loop detection

When the above described automatic query/post-loop detection fails (which can happen for a myriad of reasons), the facetwp-template class will not be not placed on the post-loop parent element. Without it, FacetWP does not know which content to dynamically replace, the facets will not work and the listing will not get updated when filtering.

The solution is easy: manually place the facetwp-template class on an element that (directly) surrounds the post loop, in the template file that contains the loop:

How to use custom PHP code?

PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info

?> <div class="facetwp-template"> <?php if ( $my_query->have_posts ) : while ( $my_query->have_posts() ) : $my_query->the_post(); ?> <h1><?php the_title(); ?></h1> <div><?php the_content(); ?></div> <?php endwhile; else : _e( 'Sorry, no posts matched your criteria.' ); endif; wp_reset_postdata(); ?> </div>

As can be seen in the above code, the facetwp-template class needs to be placed on a container element (usually a <div>) that surrounds the loop code, preferably on the direct parent element. If there is no container element, you can add one yourself.

Where to place the custom query?

It’s recommended to place your custom query on a single page or post, and not on a WP archive page. If you do place it on an archive page, keep the following in mind:

Using a custom WP_Query on a WP archive

We often see developers using a custom WP_Query in WP archive templates, in order to replace WP’s default archive query (or to change its arguments, like the number of posts per page). The downside of this approach is that the database is getting queried twice, adding unnecessary overhead. It also breaks plugins that rely on WP’s default archive query.

A more efficient approach is to modify the already existing archive query itself, with a pre_get_posts filter, instead of using a separate custom WP_Query.

However, if you have a custom WP_Query on a WP archive page, and you don’t want or cannot move the custom WP_Query from the WP archive, you have to tell FacetWP explicitly which query (not) to use.

FacetWP has built-in query detection that determines which query on the page is the main query to use for filtering. On WP archive pages, FacetWP by default will always prioritize the archive query ahead of any other query on the page, including the custom WP_Query you placed on that page. This is the reason why a custom query on a WP archive will lead to unexpected results: FacetWP is using another query than the one defined in the custom query.

Using the facetwp_is_main_query hook, it is possible to force FacetWP to ignore the archive query, and use the custom query instead. Add the following code to your (child) theme’s functions.php to do this:

How to use custom PHP code?

PHP code can be added to your (child) theme's functions.php file. Alternatively, you can use the Custom Hooks add-on, or a code snippets plugin. More info

add_filter( 'facetwp_is_main_query', function( $is_main_query, $query ) { if ( $query->is_archive() && $query->is_main_query() ) { $is_main_query = false; } return $is_main_query; }, 10, 2 );

See also