How to pre-filter Listing Builder listing queries with dynamic URL tags
This tutorial describes how to dynamically pre-filter a Listing Builder listing’s “base” query (the query before any facet filtering), based on the page URL.
This can be done by using so-called “dynamic URL tags” in a Listing Builder listing. Dynamic URL tags pull query filter values from the page URL that the listing is located on, and pass them to the listing’s query parameters. These values can come from two things:
- one or more segments of the page URI (e.g.
city/amsterdam), or - one or more of the
GETvariables in the URL (e.g.?city=amsterdam).
Dynamic URL tags make it possible to use the same listing on different pages, with its base query adapting itself automatically to the page URI. This can be done using the http:uri URL tag.
Another application is to automatically pre-filter the base query of the listing based on the GET variables added to the (incoming) URL of the page. This can be done using the http:get URL tag.
Let’s first look at what query filters are, and how they work exactly:
What are Listing Builder listing query filters?
When building a listing’s query in the “Query” tab of a Listing Builder listing, you can add one or more query filtering rules by clicking “Add query filter”. Here is an example with three query filter rules:

Depending on the chosen field type, these query filters resolve to a post__in, meta_query, or tax_query query argument, which you can see in Dev mode, after click the red “Convert to query args” button.
Static and dynamic query filters
The listing’s query filter rules generally are static: you define them in the Listing Builder once, and they determine which posts are retrieved by the listing’s “base” query, no matter what kind of template or page you add the listing to.
The base query determines which posts are retrieved before using any facets. When a facet choice is clicked by a user, the query changes dynamically. When a Reset facet is used, all selections are undone, or the page is reloaded, the query returns to its statically defined base state.
If a listing is added to a WP archive template, there is a way to dynamically adapt the listing’s base query to the archive template the listing is on, so that you can use the same listing (shortcode) on (for example) all taxonomy term archive pages. This can be done by using the facetwp_template_use_archive hook. With this hook in place, FacetWP will automatically pre-filter the listing’s base query, based on the archive template’s current category, tag, taxonomy term, author, or search term(s), which is/are injected into the query arguments.
However, the facetwp_template_use_archive only works on category, tag, taxonomy term, author, or search archive templates. What if you want to dynamically adapt the listing query on non-archive pages, based on the URL?
The rest of this tutorial describes a way to do that: by pulling values from the URL of the page with so-called “dynamic URL tags” that can be used in the listing’s query filter rule(s).
Using dynamic URL tags
Dynamic URL tags let you pass values from the listing’s page URL into the listing query filter rule(s).
This makes it possible to use the same listing on different pages, with its query adapting itself automatically to the page URI. This can be done using the http:uri tag.
Another useful application is to automatically pre-filter the base query of the listing on a page based on the GET variables added to the (incoming) URL. This can be done using the http:get tag.
Both of these dynamic URL tags can be added as the value of a query builder filter row. Here is a quick example of a query filter row, with an http:uri URL tag as value. The :1 in the tag is explained below.

There are basically two types of dynamic URL tags: http:uri and http:get. Both can be used in several ways, as demonstrated in the examples below.
The http:uri URL tag
The http:uri URL tag gives access to the “URI” of the page. The URI (Uniform Resource Identifier) is not the same as the URL (Uniform Resource Locator). The URI is the part of the URL after the domain name, and without the beginning and ending slashes.
Say you have this URL:
https://facetwp.com/demo/state-parks/
Then the URI is: demo/state-parks. This URI has two “segments”: demo and state-parks, separated by a /.
The whole URI, and each of its segments, can be accessed with the http:uri URL tag:
| Tag | Resolves to | Example |
|---|---|---|
http:uri |
The full URI. | demo/state-parks |
http:uri:N |
The N-th path segment, split on /, starting at 0. |
http:uri:0 → demohttp:uri:1 → state-parks |
http:uri:-N |
Same, but the segment counted from the end (negative index); -1 is the last segment of the URI. Generally safer than using a positive number (see the banner below). |
http:uri:-1 → state-parks |
Example: re-use one listing on multiple sibling content pages
Say you have a directory site with a venue post type containing venues in The Netherlands. Each city has its own page, with URLs like /guides/amsterdam/, /guides/utrecht/, and /guides/the-hague/. Each of these is a real WordPress page, with its own intro copy, images, and specific layout. Below the editorial content, every page has a Listing Builder listing of venues in that city.
If there are a lot of city pages, it would be impractical to create a separate listing for each. Using a dynamic URL tag, you can create one listing and re-use it on each city page. The listing retrieves all venue posts, pre-filtered by city, by the URL tag that pulls the city name from the URI.
To link the city in the page URI to the venue posts, these posts need a custom field city, set to the venue’s city. Then you can create a query filter rule based on that custom field. In the filter rule, you can use the http:uri:-1 tag to dynamically filter the listing query to the last item in the URI, which is the name of the city.
On the page with the URI guides/amsterdam, the tag http:uri:-1 will resolve to amsterdam, and on guides/utrecht, it will resolve to utrecht:

http:uri:-1 dynamic URL tag in a custom-field-based listing query filter.So with this URL tag in place, the same listing will show venues in Amsterdam on the /guides/amsterdam/ page, and venues in Utrecht on the /guides/utrecht/ page, etc.
Note that the URI only changes the “base” query of the listing. Any facets on these pages can be used to further filter down the venues in that city.
So far, we’ve used a custom field containing the city, but this also works with a taxonomy. In this example, you could have a cities taxonomy, and the http:uri:-1 tag would resolve a URI like guides/amsterdam to the taxonomy term slugamsterdam:

http:uri:-1 dynamic URL tag in a taxonomy-based listing query filter.Architecturally, instead of using a single WordPress page for each city, you could also opt to make a cities taxonomy, in which case each city already would automatically have its own taxonomy term archive template. If you choose that route, instead of using dynamic URL tags, you can use the facetwp_template_use_archive hook, which pre-filters the listing query by the current taxonomy term (city) of the archive query.
Example: using multiple segments from the URI in one listing
Building on the directory example above, suppose you also offer “compare” pages that put two cities side by side: /compare/amsterdam/utrecht/, /compare/rotterdam/the-hague/, and so on. Each is a single WordPress Page, and each shows venues from both cities in one listing.
To create a listing that pre-filters by any two cities in the URL, you can add a query filter with the IN compare and two separate http:uri URL tags:

http:uri dynamic URL tags in a custom-field-based listing query filter.For the /compare/amsterdam/utrecht/ page, http:uri:1 resolves to amsterdam and http:uri:2 to utrecht, so the listing returns venues in either city.
The advantage of using a dynamic URL tag here is that one listing template drives every comparison page, whatever pair of cities is in the URL. The order in the URL does not matter in this example: /compare/amsterdam/utrecht/ will show the same posts as /compare/utrecht/amsterdam/.
Also with multiple http:uri tags in one rule, you can use a taxonomy instead of a custom field, as shown in the example above.
The http:get URL tag
The http:get URL tag gives access to the GET variables in the URL. The GET or query variables are parts of the URL after the ?. For example, this URL has one GET variable:
https://facetwp.com/demo/state-parks/?type=reserve
Each GET variable has a name (in this example type) and a value (in this example reserve).
A URL can also have multiple GET variables, which are separated with an ampersand (&), for example:
https://facetwp.com/demo/state-parks/?state=oregon&type=reserve
The http:get URL tag can be used as follows:
| Tag | Resolves to | Example |
|---|---|---|
http:get:name |
The name of a query variable in the URL. |
http:get:state → oregonhttp:get:type → reserve |
Example: link to a pre-filtered listing from anywhere
In the same directory site as in the previous examples, say you have a /guides/ page with all venues in The Netherlands. On the site’s front page, you have a section with buttons for each city: Amsterdam, The Hague, Utrecht, etc. Each of the buttons links to the same /guides/ page with a GET variable: /guides/?city=amsterdam.
To get the listing on this /guides/ to only show venues in the city in the incoming link, each venue post needs a custom field city set to the venue’s city. Then you can create a query filter rule that uses a http:get:city URL tag to resolve to the city GET variable in the URL:

http:get dynamic URL tag in a custom-field-based listing query filter.So with this URL tag in place, the listing on the /guides/ page will show venues in Amsterdam when the clicked front-page button linked to /guides/?city=amsterdam, and venues in Utrecht when the clicked button linked to /guides/?city=utrecht, etc.
The GET variable only changes the “base” query of the listing. Any facets on this page can be used to further filter down the venues in that city (and will add their own prefixed GET variables when used).
Also with this type of dynamic URL tag, you are not limited to custom fields. You can also use a taxonomy. In this example you could have a cities taxonomy, and the http:get:city tag would resolve a GET variable like /?city=amsterdam to the taxonomy term amsterdam:

http:get dynamic URL tag in a taxonomy-based listing query filter.Example: using multiple GET query variables in one listing
Building on the directory example above, suppose you want to offer a “compare” page that can put any two cities side by side: /compare/?city1=amsterdam&city2=utrecht.
To create a listing that pre-filters by any two cities in the URL’s two GET variables, you can add a query filter with the IN compare and two separate http:get URL tags:

http:get dynamic URL tags in a custom-field-based listing query filter.For the /compare/?city1=amsterdam&city2=utrecht link, http:get:city1 resolves to amsterdam and http:get:city2 to utrecht, so the listing returns venues in either city.
The result is that one listing template drives every combination of cities added in the URL’s two GET variables. The order in the URL does not matter in this example: /compare/?city1=amsterdam&city2=utrecht will show the same posts as /compare/?city1=utrecht&city2=amsterdam.
Also with multiple http:get tags, the available query filters are not limited to custom fields. You can also use a taxonomy, as shown in the example above.
Debugging the query
If you’re confused about what the URL tags are doing exactly under the hood, you can enable Debug Mode in the settings, open your page with the listing, and type FWP.settings.debug.query_args in the browser console. This will show an object with all query parameters that are used. For an overview of WP_query parameters, see the WP_Query documentation.
If you are using a custom-field-based query filter rule, you’ll see that the query has a meta_query. For example, this filter rule:

http:get dynamic URL tags in a custom-field-based listing query filter.on a the page with the URL /compare/?city1=amsterdam&city2=utrecht, will result in the listing using this meta_query:
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
"meta_query" => [ [ "key" => "city", "compare" => "IN", "type" => "CHAR", "value" => [ "amsterdam", "utrecht" ] ] ]
And if you are using a taxonomy-based query filter rule, you’ll see that the query has a tax_query. For example, this filter rule:

http:get dynamic URL tags in a taxonomy-based listing query filter.on a the page with the URL /compare/?city1=amsterdam&city2=utrecht, will result in this tax_query:
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
"tax_query" => [ [ "taxonomy" => "cities", "field" => "slug", "operator" => "IN", "terms" => [ "amsterdam", "utrecht" ] ] ]
Keep in mind that for hierarchical taxonomies, WP includes child terms. This means that using a parent slug will match products in its child categories.
Using dynamic URL tags in Dev mode
It is also possible to use the above-described http:uri and http:get URL tags when using a Listing Builder listing in Dev mode, which allows more complex query arguments.
For custom-field-based query filtering, you can construct the needed meta_query as follows:
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
<?php return [ "post_type" => [ "product" ], "post_status" => [ "publish" ], "meta_query" => [ [ "key" => "city", "compare" => "IN", "type" => "CHAR", // single http:uri tag "value" => FWP()->builder->parse_uri_tags( [ "http:uri:-1" ] ) // OR, multiple http:uri tags //"value" => FWP()->builder->parse_uri_tags( [ "http:uri:1", "http:uri:2" ] ) // OR, single http:get tag //"value" => FWP()->builder->parse_uri_tags( [ "http:get:city" ] ) // OR, multiple http:get tags //"value" => FWP()->builder->parse_uri_tags( [ "http:get:city1", "http:get:city2" ] ) // For >, <, >=, <=, LIKE, NOT LIKE: pass a single string, not an array. E.g.: // "compare" => ">", // "type" => "DECIMAL(16,4)", // "value" => FWP()->builder->parse_uri_tags( "http:get:min_price" ) ] ], "orderby" => [ "title" => "ASC" ], "posts_per_page" => 30 ];
And for taxonomy-based query filtering, you can construct the needed tax_query as follows:
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
<?php return [ "post_type" => [ "product" ], "post_status" => [ "publish" ], "tax_query" => [ [ "taxonomy" => "product_cat", "field" => "slug", "operator" => "IN", // single http:uri tag "terms" => FWP()->builder->parse_uri_tags( [ "http:uri:-1" ] ) // OR, multiple http:uri tags //"terms" => FWP()->builder->parse_uri_tags( [ "http:uri:1", "http:uri:2" ] ) // OR, single http:get tag //"terms" => FWP()->builder->parse_uri_tags( [ "http:get:city" ] ) // OR, multiple http:get tags //"terms" => FWP()->builder->parse_uri_tags( [ "http:get:city1", "http:get:city2" ] ) // For >, <, >=, <=, LIKE, NOT LIKE: pass a single string, not an array. E.g.: // "compare" => ">", // "type" => "DECIMAL(16,4)", // "value" => FWP()->builder->parse_uri_tags( "http:uri:min_price" ) ] ], "orderby" => [ "title" => "ASC" ], "posts_per_page" => 30 ];
Note that for both custom-field-based and taxonomy-based query filtering you can pass an array to FWP()->builder->parse_uri_tags(), no matter if you use a single or multiple dynamic URL tags. The only exception is if you use range or pattern comparisons like >, <, >=, <=, LIKE, or NOT LIKE. In those cases, you must pass a single string, not an array.
Also when using Dev mode, make sure to read the notes and caveats mentioned below.
Using dynamic URL tags with serialized custom fields
Custom fields that can have multiple values, like an ACF Checkbox field, store these values as a serialized array. In the database, an example postmeta row for an ACF Checkbox field with three selected city values may look 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
a:3:{i:0;s:9:"amsterdam";i:1;s:7:"utrecht";i:2;s:9:"the-hague";}
The (non-Dev-mode) standard IN comparison of a query filter rule cannot read from serialized values. To match a serialized value, in Dev mode you can use a LIKE comparison, as shown in the following example.
Because LIKE is a so-called scalar ("single value") comparison, you cannot pass multiple dynamic URL tags in an array. So, make sure to pass a single bare string to FWP()->builder->parse_uri_tags().
If you do need multiple values, each value must get its own meta_query argument, and you'd need a relation argument to determine the OR or AND logic used between them. See this example for how that should be set up.
Make sure to wrap the double-quoted value with extra single quotes if you want the LIKE comparison to make an exact match with the custom field (or term) value. If you also want partial matches, don't wrap it in single quotes. For example, line 11 will match an ACF Checkbox field value of amsterdam, but not amsterdam-south. If you use line 12 instead, both amsterdam and amsterdam-south will match:
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
<?php return [ "post_type" => [ "product" ], "post_status" => [ "publish" ], "meta_query" => [ [ "key" => "city", // E.g. an ACF Checkbox field "compare" => "LIKE", "type" => "CHAR", "value" => '"' . FWP()->builder->parse_uri_tags( "http:uri:-1" ) . '"' // Wrap the double quotes with single quotes to get exact matches. // "value" => FWP()->builder->parse_uri_tags( "http:get:city" ) // Or, use the value without all quotes, to get partial matches. ] ], "orderby" => [ "title" => "ASC" ], "posts_per_page" => 30 ];
Using dynamic URL tags on archive templates
You may want to use a Listing Builder listing on an archive template, instead of using the archive template's loop that comes with your theme.
If you choose that route, you can use the facetwp_template_use_archive hook to automatically pre-filter the listing's base query, based on the archive template's current category, tag, taxonomy term, author, or search term(s), which then will be injected into the listings's query arguments. This hook works on category, tag, taxonomy term, author, and search archive templates.
Alternatively, you could use a dynamic URL tag:
Using dynamic URL tags on taxonomy term archives
For example, say you have set up a cities taxonomy. Each city will automatically have its own archive page, with a URI like cities/amsterdam, cities/utrecht, etc.
For taxonomy term archives, you can use a http:uri:-1 tag to match the term slug in the last segment of the taxonomy term archive's URI. For example, this query filter will match amsterdam on a term archive page with the URI city/amsterdam:

http:uri:-1 dynamic URL tag in a taxonomy-based listing query filter.You can also do this in Dev mode, with a tax_query argument doing exactly the same:
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
<?php return [ "post_type" => [ "post" ], "post_status" => [ "publish" ], "tax_query" => [ [ "taxonomy" => "cities", "field" => "slug", "operator" => "IN", "terms" => FWP()->builder->parse_uri_tags( [ "http:uri:-1" ] ) ] ], "orderby" => [ "title" => "ASC" ], "posts_per_page" => 30 ];
In both "visual mode" and Dev mode, it is important to also tell FacetWP to ignore the taxonomy term archive query itself, because FacetWP's query detection automatically prioritizes the archive query on archive templates. To do so, add the following snippet to your (child) theme's functions.php:
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
// Ignore the native archive query on all taxonomy term archives add_filter( 'facetwp_is_main_query', function( $is_main_query, $query ) { if ( ( $query->is_category() || $query->is_tag() || $query->is_tax() ) && $query->is_main_query() ) { // true only on taxonomy term archives $is_main_query = false; } return $is_main_query; }, 10, 2 );
Using dynamic URL tags on author archives
For author archives, you could use a http:uri:-1 tag to match the author_name in the last segment of the author archive's URI, for example author/john:

author_name-based http:uri:-1 dynamic URL tag in a listing query filter, to match the author name in the last segment of the URI of author archives.In Dev mode, you can do the same 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
<?php return [ "post_type" => [ "post" ], "post_status" => [ "publish" ], "author_name" => FWP()->builder->parse_uri_tags( "http:uri:-1" ), "orderby" => [ "date" => "DESC" ], "posts_per_page" => 30 ];
In both "visual mode" and Dev mode, it is important to also tell FacetWP to ignore the author archive query itself, because FacetWP's query detection automatically prioritizes the archive query on archive templates. To do so, add the following snippet to your (child) theme's functions.php:
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_author() && $query->is_main_query() ) { // true only on author archives $is_main_query = false; } return $is_main_query; }, 10, 2 );
Using dynamic URL tags on search archives
On a search archive page, with a GET variable like /?s=keyword, you can't use a Listing Builder listing in "visual mode", because the search keywords in the s GET variable are not available as a field to choose in a listing query filter rule. But you can use a http:get:s URL tag in Dev mode, as follows:
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
return [ "post_type" => [ "post" ], "post_status" => [ "publish" ], "s" => FWP()->builder->parse_uri_tags( "http:get:s" ), "orderby" => [ "date" => "DESC" ], "posts_per_page" => 30 ];
It is important to also tell FacetWP to ignore the search archive query itself, because FacetWP's query detection automatically prioritizes the archive query on archive templates. To do so, add the following snippet to your (child) theme's functions.php:
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_search() && $query->is_main_query() ) { // true only on search archives $is_main_query = false; } return $is_main_query; }, 10, 2 );
When to use what
If your Listing Builder listing is on a WordPress single post or page, you can let it dynamically adapt its query to the URL with one of the available dynamic URL tags, as explained above, in "visual mode" as well as in Dev mode. The above-mentioned facetwp_template_use_archive hook is not relevant in this case, as it only works on WP archive pages.
If you want to use a Listing Builder listing on a WP archive page, to let the query dynamically adapt itself to the archive query, in general we recommend using the facetwp_template_use_archive hook. It's the easiest solution: it works on all archive types and is only one line of code in your functions.php.
Alternatively, on WP archives, you could use a dynamic URL tag instead of the facetwp_template_use_archive hook. A reason could be if you want to create complex custom query arguments that you prefer to build in the Listing Builder, in "visual mode" or in Dev mode, instead of customizing the native archive query with a pre_get_posts hook.
Important notes and caveats
- URLs with no variable(s) - If a URL does not have a
GETvariable (so/guides/or/compare/in above examples), the listing will have no results when using a taxonomy-based query filter rule. When using a custom-field-based query filter rule, whether there are results will depend on how the field is stored. If posts have the field stored in the database, but with an empty value, the listing will retrieve them. This difference is caused by how WP treats atax_queryversus ameta_query. If WordPress can't match an empty slug to a term, it short-circuits the whole query. See the debug section above for more info on these query parameters. - Exact matches only - The URL tag must match the entire URI segment or value of the
GETvariable exactly. You can't combine it with a prefix or static text, or append a path. For a taxonomy term, this may be obvious (because it will use the term slug), but for a custom field, not. For example, for a URI/city/the-hagueor aGETvariable/?city=the-hagueto match a post, the post'scitycustom field must containthe-hague, notThe Hague. - Matches by slug only - Values match by slug, not by ID. A URI path segment or
GETvalue is used exactly as-is. - No validation - The
http:gettag reads the URL parameter as-is. The value is not validated, so don't rely on it for access-sensitive query logic. - Using serialized custom fields - In a "visual" (non-Dev-mode) Listing Builder listing, custom-field-based query filter rules will only work with basic text-based custom fields. Custom fields that can have multiple values, like an ACF Checkbox field, store these values as a serialized array. The
INcompare of the filter rule cannot read from these serialized values. To match a serialized value, you can use aLIKEcomparison. However, at the time of writing, the "visual" Listing Builder does not have aLIKEcompare option. So to get dynamic tags working with serialized fields, you'll have to switch to Dev mode, and useLIKEin ameta_queryargument as explained above. - Save URL tags correctly and separately - Each URL tag in the query filter rule must be a gray "pill", or they won't work. After typing the tag, make sure to press Enter/Return, or select it from the dropdown that appears while typing. Also, don't try to combine multiple tags in a single value. Each tag must be in its own separate pill, or they won't resolve. The tags and their "pills" must look like this:

Dynamic URL tags in a listing query filter must each have a gray pill shape. Press Return/Enter after typing to create the gray pill from a typed tag. and NOT like this:

An incorrectly entered dynamic URL tag in a custom-field-based listing query filter. Press Return/Enter after typing to create the gray pill from a typed tag. - Pre-select facet choices - All of the above implementations of dynamic URL tags pre-filter the base query. But they do not automatically pre-select any facet choices. To accomplish that, see this tutorial on how to use the
facetwp_preload_url_varshook to do that.