Vue Storefront is now Alokai! Learn More
Load dynamically facets

Load dynamically facets

This module offers possibility load dynamically filter facets. This feature enhance the user experience by displaying maximum relevant facets on initial load. This behaviour minimize loading multiple facets at once and provide possibility to load more available facets when needed.

This feature can be easily enabled just by adding hook/composable into Filter and CategoryTree components.

Facet config note

In order dynamic load of facets would work properly we need to define sortCriteria in out facets.config.ts file. This could be e.g sortCriteria: 'score'. The reason is that FacetManager sortCriteria propery has default atLeastOneValue and Facet which is used internally to download more facets uses default automatic. Which means that if sortCriteria property is not set, FacetManager loads different set of values and then when is loaded more value with controller method showMoreValues new set of facets is downloaded because of different sortCriteria.

For all filters beside Category, one useFacets hook needs to be applied in facets.tsx component.

// storefront-unified-nextjs/components/products-listing/facets.tsx
import { CoveoPriceItem } from '@sf-modules/coveo'; import { CoveoPriceItem, useFacets } from '@sf-modules/coveo';  
function Facet({
  containerClassName,
  expandableListProps,
  facet,
  itemRenderer: FacetItem,
  multiSelect = false,
}: FacetProps) {
  // ...

  const {     canShowMore: isButtonVisible,     itemsToDisplay: itemsToRender,     showMore: isExpanded,     toggleShowMore: toggle,   } = useFacets({ facetName: facet.name, max: 5 }); 
  return (
    // ...
      <ExpandableList {...expandableListProps}>        {facet.values.map((item) => (       <ExpandableList        {...expandableListProps}        isButtonVisible={isButtonVisible}        isExpanded={isExpanded}        toggle={toggle}      >        {itemsToRender.map((item) => (       // ...
  );
}

For Category filters changes have to be made in category-tree.tsx component. We need to import useCategoryFacets hook and apply its returned variables.

// storefront-unified-nextjs/components/products-listing/category-tree.tsx
'use client'; import { useCategoryFacets } from '@sf-modules/coveo'; // ...

export default function CategoryTree({ categoryData, facet, slugs }: // [!code --]CategoryTreeProps) {
export default function CategoryTree({ categoryData, slugs }: CategoryTreeProps) {   // ...
  const categories = facet?.values ?? [];   // ...

  const {     canShowMore: isButtonVisible,     itemsToDisplay: itemsToRender,     showMore: isExpanded,     toggleShowMore: toggle,   } = useCategoryFacets({ max: 5 }); 
  return (
    // ...
      <ExpandableList>         {categories.map(({ label, value }) => (       <ExpandableList isButtonVisible={isButtonVisible} isExpanded={isExpanded} toggle={toggle}>        {itemsToRender.map(({ label, value }) => (   );
}

Additionally since category-tree.tsx component is now client side only, we have to add CategoryTree namespace to i18n provider, we can do this inside products-listing-page.tsx component.

// storefront-unified-nextjs/components/products-listing/products-listing-page.tsx

export default function ProductsListingPage({
  // ...

  return (
    <NextIntlClientProvider
      messages={pick(messages, 'Facets', 'FiltersContainer', 'ExpandableList', 'SortByDropdown')}       messages={pick(messages, 'Facets', 'FiltersContainer', 'ExpandableList', 'SortByDropdown', 'CategoryTree')}     >
  )
}

Lastly we have to modify expandable-list.tsx component.

// storefront-unified-nextjs/components/ui/expandable-list.tsx
import { SfButton, useDisclosure } from '@storefront-ui/react'; import { SfButton } from '@storefront-ui/react'; 
export interface ExpandableListProps extends PropsWithChildren {
  // ...
  isButtonVisible?: boolean;   isExpanded?: boolean;   toggle?: () => void; }

export default function ExpandableList({
  buttonClassName,
  children,
  id,
  initialExpanded = false,   maxCollapsedItems = 5,   isButtonVisible,   isExpanded,   toggle, }: ExpandableListProps) {
  const t = useTranslations('ExpandableList');
  const isButtonVisible = Array.isArray(children) && maxCollapsedItems < children.length;   const { isOpen: isExpanded, toggle } = useDisclosure({ initialValue: initialExpanded });   const idPart = id ? `${id}-` : '';

  return (
    <>
      {Children.map(children, (child, index) => {         if (isExpanded || index < maxCollapsedItems) {           return child;         }         return null;       })}      {Children.map(children, (child) => child)}