FacetWP includes built-in support for Advanced Custom Fields (ACF). It supports basic field types (Text, Select, Checkbox, etc.), Relationship fields and even Repeater sub-fields.

Create your field group

The first step is to create an ACF field group. In the screenshot below, we’re adding a new “Event Fields” group, and attaching it to the Event post type.

ACF field group

Use your field group

After you’ve added your field group, the next step would be to add some content. Edit some of your posts to make sure that your fields are appearing correctly.

post edit screen

Create your facet

After you’ve added some content using your new custom fields, go to Settings > FacetWP, click the Facets tab, then click the Add New button.

For the facet’s Data source, scroll down until you see the ACF heading, then select the appropriate choice.

FacetWP data source dropdown

Alternatively, use the built-in search box for quickly finding the desired field.

FacetWP data source search

Using Repeater fields

Facets can be added using data from repeater sub-fields.

Repeater values are tied back to the parent post. This means that repeater rows aren’t directly filterable.

For example, let’s say you have a “Speakers” facet based on a repeater field:

If the user selects “Jim”, the other choices (Bob, Dan) will remain too. This is because the 3 choices are tied to the parent post.

Using Advanced Custom Fields with a Map facet or Proximity facet

If you are using Advanced Custom Fields with a Map facet or Proximity facet, you can use ACF’s Google Map field type to attach a location to your posts. If you use this ACF field, in the Map / Proximity facet’s “Data source” settings choose this field underneath the “ACF” header.

If your locations don’t show up on the map when using an ACF Google Maps field, make sure the field is directly in the main Field Group, or at most one level deep in a nested Field Group. Google Maps fields in Field Groups that are nested deeper than one level, will not work.

Using multiple data sources / locations per post

To use an ACF Google Maps field in an ACF Repeater field, choose the Google Maps field as data source ('<code>my_map<code>' in this example).
To use an ACF Google Maps field in an ACF Repeater field, choose the Google Maps field as data source (my_map in this example).

It is possible to index multiple locations per post, by using a multi-value custom field, like an ACF repeater field, or for example a checkboxes/dropdown field (if you have a select number of locations to choose from).

If you use an ACF Repeater field with a Google Maps sub field, you need to choose the Google Maps sub field's name from the Map facet's Data Source dropdown (my_map in the example to the right).

Make sure to choose the field under the "ACF" heading in the Data Source dropdown.

Using an ACF Checkbox field as meta_query filter

If you are using an ACF Checkbox field to filter your (custom) query by, you will notice that this does not work out of the box. This is because in order to handle multiple values, ACF stores Checkbox values as a serialized string in the database. Serialized values are difficult to do anything with.

In a Listing Builder listing you would probably try to get it working with a query filter, like this:

Using an ACF Checkbox field as query filter.
Using an ACF Checkbox field as query filter. This will not work!

The above will not work with ACF Checkbox fields.

To be able to use a Checkbox field to filter the query, first click the red "Convert to query args" button at the bottom, which will convert the query settings to PHP query arguments. With the above example settings, the arguments will look as follows. Note that this will still not work:

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 /* THIS DOES NOT WORK, DON'T USE! */ return [ "post_type" => [ "product" ], "post_status" => [ "publish" ], "meta_query" => [ [ "key" => "my_acf_checkbox_field", "compare" => "IN", "type" => "CHAR", "value" => [ "my_value" ] ] ], "orderby" => [ "title" => "DESC" ], "posts_per_page" => 10 ];

To make these arguments work with a serialized Checkbox field value, you need to make three manual changes:

  1. Change the compare value from IN to LIKE.
  2. Remove the array brackets surrounding the value.
  3. Wrap the double-quoted value with single quotes.

The result should 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

<?php return [ "post_type" => [ "product" ], "post_status" => [ "publish" ], "meta_query" => [ [ "key" => "my_acf_checkbox_field", "compare" => "LIKE", // Changed to "LIKE" "type" => "CHAR", "value" => '"my_value"' // Removed the array brackets. And wrapped the double quotes with single quotes. ] ], "orderby" => [ "title" => "DESC" ], "posts_per_page" => 10 ];

If you need to filter by multiple Checkbox values, you cannot add them as an array. Each value will need its own meta_query argument. You'll also need to add a relation argument:

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" => [ 'relation' => 'OR', // Needed for multiple values [ "key" => "my_acf_checkbox_field", "compare" => "LIKE", "type" => "CHAR", "value" => '"my_value"' ], [ "key" => "my_acf_checkbox_field", "compare" => "LIKE", "type" => "CHAR", "value" => '"my_other_value"' ] ], "orderby" => [ "title" => "DESC" ], "posts_per_page" => 10 ];

If you have too many values to do this, it would be better to use a custom taxonomy instead of a Checkbox field.

Using a Date Picker or Date Time Picker field

FacetWP supports the ACF Date Picker and Date Time Picker field types. It will automatically process the way these fields store dates in the wp_postmeta database table (as YYYYMMDD for Date Picker, and YYYY-MM-DD HH:MM:SS for Date Time Picker). These fields' "Display Format" and "Return Format" settings have no influence on this.

Facets that use a Date Picker field as their data source will by default display their date options in a YYYY-MM-DD format. To reformat this to January 17, 2024 for example, add the following snippet to your (child) theme's functions.php. Make sure to re-index after adding these snippets:

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_index_row', function( $params, $class ) { if ( 'my_date_facet' == $params['facet_name'] ) { // Replace 'my_date_facet' with the name of your facet $raw_value = $params['facet_value']; $params['facet_display_value'] = date( 'F j, Y', strtotime( $raw_value ) ); // Use "January 17, 2024" format for the facet's display value. For other date formats, see: https://www.php.net/manual/en/function.date.php } return $params; }, 10, 2 );

Facets using a Date Time Picker field will automatically display their date options in a YYYY-MM-DD HH:MM:SS format. To reformat this to January 17, 2024 at 17:15:05 PM for example, you can use the following snippet:

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_index_row', function( $params, $class ) { if ( 'my_date_facet' == $params['facet_name'] ) { // Replace 'my_date_facet' with the name of your facet $raw_value = $params['facet_value']; $params['facet_display_value'] = date( 'F j, Y \a\t H:i:s A', strtotime( $raw_value ) ); // Use "January 17, 2024 at 17:15:05 PM" format for the facet's display value. For other date formats, see: https://www.php.net/manual/en/function.date.php } return $params; }, 10, 2 );

To use any other date format, see the examples on the PHP.net date() documentation page.

Note that the Date Range facet has a Display format setting to format its date display.

Using a Color Picker field

If you use an ACF Color Picker field as a facet's data source, you may want the use the color value itself to style the facet choice label. You could for example set a CSS text color or background-color to the label, or create a color swatch element before or after the label text.

You can do this with the facetwp_facet_display_value hook. Add the following snippet to your (child) theme's functions.php to give the facet choice label a CSS background-color based on the display value of the label.

Note that the snippet uses facet_display_value for the color value and not facet_value, because the facet_value (the technical value as shown in the URL) will be hashed to a random string of numbers (because of unsafe characters in the URL).

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_facet_display_value', function( $label, $params ) { if ( 'my_color_facet' == $params['facet']['name'] ) { // Replace 'my_color_facet' with the name of your color-picker-based facet. $val = $params['row']['facet_display_value']; // Use the display value here to get the unhashed hex or rgb(a) color value. $label = '<span style="background-color:' . $val . ';">' . $label . '</span>'; // Add a span to the facet choice with the color as CSS background color. } return $label; }, 20, 2 );

The above code works both with the "Enable Transparency" setting disabled (outputting a hex string) and enabled (outputting an RGBA string).

If you have the "Enable Transparency" setting disabled (so the field is outputting a hex string) and you want to prevent hashing/sanitizing of color values in the URL, you can remove the hex color's # character (which causes the hashing) with WP's sanitize_hex_color_no_hash() function from the facet_value with the facetwp_index_row hook, while indexing. The following snippet shows how to do that. Make sure to re-index after adding this snippet and making changes to it.

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_index_row', function( $params, $class ) { if ( 'my_color_facet' == $params['facet']['name'] ) { // Replace 'my_color_facet' with the name of your color-picker-based facet. // Get the color value (with the '#') $colorvalue = $params['facet_value']; // Remove the '#' from the hex color value to prevent hashed/sanitized color values in the URL $params['facet_value'] = sanitize_hex_color_no_hash( $colorvalue ); } return $params; }, 10, 2 );

Use a color taxonomy with a Color Picker term field

If you want readable color names (e.g. "green") in your facet choice labels, instead of the actual hex color values, you could create a custom "color" taxonomy (containing color names as terms), and set your facet's data source to use this taxonomy. Then, set up an ACF Color Picker field as term meta field, by setting the field's Field Group's location to this "color" taxonomy.

This will make the facet choices display color names, but you cannot yet use the color values from the Color Picker field yet to style them. To set this up, add the two snippets below to your (child) theme's functions.php.

The first part indexes the hex color value of the term meta field into the facet choice's facet_value (the technical value as shown in the URL). While doing so, in line 11 we remove the # character from the color value, with WP's sanitize_hex_color_no_hash() function, to prevent the color value from being hashed/sanitized. After re-indexing, the color value will now show as a 6-digit color code without the # (e.g. 000000 for black), in the URL when the facet choice (e.g. "black") is selected.

Next, we use the facetwp_facet_display_value hook to set a CSS background-color. For this to work, we first get the color value from facet_value value. Then we add the # character back in, on line 20 to get a valid hex color value. The result is a facet with color names as choice labels, styled with a CSS background-color set on each of the choice labels:

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_index_row', function( $params, $class ) { if ( 'my_color_facet' == $params['facet']['name'] ) { // Replace 'my_color_facet' with the name of your color-picker-based facet. // Get the taxonomy term $term_id = (int) $params['term_id']; // Get the color value from an ACF Color Picker term meta field $colorvalue = get_term_meta( $term_id, 'term_color_picker_field', true ); // Replace 'term_color_picker_field' with the name of the Color Picker term field // Remove the '#' from hex color value to prevent hashed/sanitized color values in the URL $params['facet_value'] = sanitize_hex_color_no_hash( $colorvalue ); } return $params; }, 10, 2 ); add_filter( 'facetwp_facet_display_value', function( $label, $params ) { if ( 'my_color_facet' == $params['facet']['name'] ) { // Replace 'my_color_facet' with the name of your color-picker-based facet. $val = $params['row']['facet_value']; // Use the facet_value here to get the unhashed hex color value, without the '#' $label = '<span style="background-color: #' . $val . ';">' . $label . '</span>'; // Add a span to the facet choice with the color as CSS background color. Needs the '#' before the color value. } return $label; }, 20, 2 );

Note that this setup only works when the "Enable Transparency" setting is disabled (so the field is outputting a hex color string). If you need this to work with RGBA color strings, you'll have to destructure the RGBA string into four values, and store them as a string (with e.g. a dash as separator) in the first snippet. Then restructure these four values into a valid RGBA string again in the second snippet.

Creating custom post types with ACF

As of ACF 6.1, it is possible to register custom post types and custom taxonomies with it.

If you are registering a custom post type this way, make sure you disable the "Exclude From Search" setting located under Post Types > Edit Post Type > Advanced Settings > Visibility:

Disable the 'Exclude From Search' setting when registering custom post types with ACF.
Disable the 'Exclude From Search' setting when registering custom post types with ACF.

This is needed because FacetWP only indexes searchable post types. If your post type is not searchable, you will get empty facets or a "The index table is empty" error.

If you enabled this setting because you don't want this post type in your search results, it is also possible to force FacetWP to index non-searchable post types, with the facetwp_indexer_query_args hook.

Using taxonomy term custom fields as facet data source

With Advanced Custom Fields you can add custom fields to taxonomy terms. (The same is true for the Pods plugin, the instructions mentioned here are the same.)

When you set such a custom term field as the data source in a facet, you will notice that the facet does not display any choices. This is because custom fields attached to taxonomy terms cannot be indexed directly by FacetWP.

However, with a bit of custom code, using a custom term field as data source is possible.

First, create the facet and set its data source to the taxonomy itself. Then, add the following code to your (child) theme's function.php. Make sure to re-index afterwards.

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

/** * This code works for term fields created in ACF and Pods. * 1. Replace "your_facet_name" with the actual facet name * 2. Replace "your_custom_field" with the actual custom field name * 3. Re-index afterwards */ add_filter( 'facetwp_index_row', function( $params, $class ) { if ( 'your_facet_name' == $params['facet_name'] ) { // Get the taxonomy term $term_id = (int) $params['term_id']; // Get the custom field value from the term $value = get_term_meta( $term_id, 'your_custom_field', true ); // Set the facet label and URL slug to the custom term field value $params['facet_display_value'] = $value; // the facet choice label // Optional, see explanation below. $params['facet_value'] = $value; // the facet URL slug, used for indexing/filtering } return $params; }, 10, 2 );

What this code does, is force FacetWP into indexing the custom term field value instead of the term name and the term slug. When using this, keep the following in mind:

Normally, when FacetWP indexes a taxonomy term, the following happens:

  • Term names are stored in FacetWP’s index table as facet_display_value and used to generate the display of the facet’s choices.
  • Term slugs are stored in FacetWP’s index table as facet_value and are used for filtering, which also means they will show up as facet slug in the URL after interacting with the facet.

The above code sets both the facet_display_value and the facet_value to the custom field's value while indexing.

Setting facet_value to the custom field value is optional. There are a few scenarios in which you may want to keep using the term slug as facet_value:

  • If your custom term field value has special characters or spaces. These will be replaced when the value is indexed, or - depending on the characters - the value will get entirely hashed into a string of random characters. If you want to keep using the term slug in the URL, do not set facet_value to the custom field value.
  • If multiple terms share the same value for the custom field, they will be indexed together as one facet choice. If you want to keep them indexed as separate choices, do not set facet_value to the custom field value.

Using ACF Block fields as facet data source

If you are using ACF Pro, you can use ACF Blocks to create custom WordPress blocks and add them to your pages.

ACF blocks can have custom fields in them. To create these, first create a Field Group and add your fields. Then, in the Field Group's Settings > Location Rules, let the Field Group show in one or more of the custom ACF blocks you created:

How to add custom fields to ACF blocks.
How to add custom fields to ACF blocks.
An ACF block field on a page.
An ACF block field on a page.

These custom fields will now appear in your block's settings, on pages/templates where you add this block, as shown in the image on the right.

By default, WordPress stores a block’s field data inside the block’s HTML comment in post_content, which is stored in the posts database table. However, FacetWP cannot use custom field data in this table to index facets. It can only index data stored in the postmeta table. So, if you create a facet and set its Data Source setting to a custom field that is connected to an ACF block, you'll see no rows indexed for this facet (in the facet overview screen), and the facet will have no choices.

To solve this, you can force ACF to store its block field data in the post-meta database table instead. This can be done by adding a "usePostMeta": "true" argument to the block's block.json file. Below is an example, with the extra argument added in line 17:

{ "name": "acf/testimonial", "title": "Testimonial", "description": "A custom testimonial block that uses ACF fields.", "style": [ "file:./testimonial.css" ], "category": "formatting", "icon": "admin-comments", "keywords": [ "testimonial", "quote" ], "acf": { "mode": "preview", "renderTemplate": "testimonial.php", "usePostMeta": "true" }, "supports": { "anchor": true } }

After adding this argument, make sure to save the posts that use this ACF block, and/or do a full re-index.

See also