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.

Taxonomy term custom fields

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 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 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 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 the 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.

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 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 postmeta 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