Implement filter suggestions (CSR)

This is for:

Developer

Use the FilterSuggestionsGenerator controller to access and display filter suggestions in your search box.

This is step 2 of the instant products and filter suggestions guide. See that page for prerequisites.

The Coveo Headless repository contains a sample implementation.

Implement a filter suggestion generator component

To display filter suggestions in your search box components, implement a component that uses the FilterSuggestionsGenerator controller. This component iterates over each facet’s filter suggestions and delegates rendering to a FilterSuggestions child component.

// src/components/filter-suggestions/filter-suggestions-generator.tsx
import type {
  CategoryFacetSearchResult,
  CategoryFilterSuggestions,
  FilterSuggestions as HeadlessFilterSuggestions,
  FilterSuggestionsGenerator as HeadlessFilterSuggestionsGenerator, 1
  RegularFacetSearchResult,
} from '@coveo/headless/commerce';
import {useEffect, useState} from 'react';
import FilterSuggestions from './filter-suggestions.js'; 2

interface IFilterSuggestionsGeneratorProps {
  controller: HeadlessFilterSuggestionsGenerator;
  onClickFilterSuggestion: ( 3
    controller: HeadlessFilterSuggestions | CategoryFilterSuggestions,
    value: RegularFacetSearchResult | CategoryFacetSearchResult
  ) => void;
}

export default function FilterSuggestionsGenerator(
  props: IFilterSuggestionsGeneratorProps
) {
  const {controller, onClickFilterSuggestion} = props;
  const [filterSuggestionsState, setFilterSuggestionsState] = useState(
    controller.filterSuggestions
  );

  useEffect(() => {
    controller.subscribe(() => {
      setFilterSuggestionsState(controller.filterSuggestions);
    });
  }, [controller]);

  if (filterSuggestionsState.length === 0) {
    return null;
  }

  return (
    <div className="FilterSuggestionsGenerator">
      {filterSuggestionsState.map((filterSuggestionsController) => {
        return (
          <FilterSuggestions
            key={filterSuggestionsController.state.facetId}
            controller={filterSuggestionsController}
            onClickFilterSuggestion={onClickFilterSuggestion}
          />
        );
      })}
    </div>
  );
}
1 Import the FilterSuggestionsGenerator type from @coveo/headless/commerce.
2 The FilterSuggestions component displays the filter suggestions for a given facet.
3 The onClickFilterSuggestion function is called when the user clicks a filter suggestion. It’s passed as a prop and is defined by the parent component. See the search box integration page.

Implement a filter suggestions component

The FilterSuggestions component renders the individual suggestion values for a single facet. It supports both hierarchical and regular facets.

// src/components/filter-suggestions/filter-suggestions.tsx
import type {
  CategoryFacetSearchResult,
  CategoryFilterSuggestions,
  FilterSuggestions as HeadlessFilterSuggestions,
  RegularFacetSearchResult,
} from '@coveo/headless/commerce';
import {useEffect, useState} from 'react';

interface IFilterSuggestionsProps {
  controller: HeadlessFilterSuggestions | CategoryFilterSuggestions; 1
  onClickFilterSuggestion: (
    controller: HeadlessFilterSuggestions | CategoryFilterSuggestions,
    value: RegularFacetSearchResult | CategoryFacetSearchResult
  ) => void;
}

export default function FilterSuggestions(props: IFilterSuggestionsProps) {
  const {controller, onClickFilterSuggestion} = props;

  const [state, setState] = useState(controller.state);

  useEffect(() => {
    controller.subscribe(() => {
      setState(controller.state);
    });
  }, [controller]);

  if (state.values.length === 0) {
    return null;
  }

  const renderFilterSuggestionButton = (
    value: RegularFacetSearchResult | CategoryFacetSearchResult
  ) => {
    return (
      <button
        onClick={() => onClickFilterSuggestion(controller, value)}
        type="button"
      >
        Search for <em>{state.query}</em>{' '}
        {controller.type === 'hierarchical' ? 'in' : 'with'}{' '}
        <b>{state.displayName}</b> <em>{value.displayValue}</em> ({value.count}{' '}
        products)
      </button>
    );
  };

  return (
    <div className="FilterSuggestions">
      <p>
        <b>{state.displayName}</b> suggestions
      </p>
      <ul>
        {state.values.map((value) => (
          <li
            key={
              'path' in value
                ? [...value.path, value.rawValue].join(';')
                : value.rawValue
            }
          >
            {renderFilterSuggestionButton(value)}
          </li>
        ))}
      </ul>
    </div>
  );
}
1 This component supports both hierarchical and regular facets.

What’s next

After building your filter suggestions components, integrate them with your search box to wire them into the user experience.