--- title: Leveraging facets, sorting, and pagination (SSR) slug: oc685396 canonical_url: https://docs.coveo.com/en/oc685396/ collection: coveo-for-commerce source_format: adoc --- # Leveraging facets, sorting, and pagination (SSR) > **Important** > > The Headless Commerce SSR utilities are in open beta. > Contact your Coveo representative for support in adopting this. Facets, sorting, and pagination are essential components of any commerce interface. These features enhance the user experience by making it easier for users to find what they're looking for when interacting with lists of products. This article explains how to implement those features in the context of a server-side rendered (SSR) commerce implementation with Coveo Headless and [Next.js](https://nextjs.org/). > **Note** > > In the following sections, you'll notice that Headless doesn't specify details about what should be returned by the Commerce API, such as which facets are available, the types of sorting criteria, and how many items are displayed per page. > Due to the [declarative nature of the Commerce API](https://docs.coveo.com/en/o6od0220/), this information is determined by the configuration associated with the Commerce API request. ## Defining and accessing target controller hooks Common functionalities like pagination, facets, and sorting are implemented using [target controller hooks](https://docs.coveo.com/en/obif0156#define-the-commerce-engine-and-controllers). Define and retrieve them as follows: ```tsx // lib/commerce-engine-config.ts import { definePagination, defineFacetGenerator, defineSort, // ... } from '@coveo/headless-react/ssr-commerce'; export default { // ... controllers: { pagination: definePagination({options: {pageSize: 9}}), <1> facetGenerator: defineFacetGenerator(), sort: defineSort(), // ... }, } satisfies CommerceEngineDefinitionOptions; ``` <1> [Define the `pagination` controller](https://docs.coveo.com/en/headless-react/latest/reference/functions/SSR_Commerce.index.definePagination.html) with the target default page size. ## Facets [Facets](https://docs.coveo.com/en/1571/) let users filter search results by specific attributes, making it easier to find relevant items. ### Facet generator The [`FacetGenerator`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.FacetGenerator.html) controller hook lets you access a list of facets that can be rendered in the UI. See [About the Facet Generator](https://docs.coveo.com/en/n9sd0159/). There are four different types of facets: * Regular * NumericalRange * Hierarchical * DateRange * Location The following code snippet showcases how to render facets using the `FacetGenerator` based on their type. ```tsx 'use client'; import {useFacetGenerator} from '@/lib/commerce-engine'; import CategoryFacet from './category-facet'; <1> import DateFacet from './date-facet'; import NumericFacet from './numeric-facet'; import RegularFacet from './regular-facet'; export default function FacetGenerator() { const {state, methods} = useFacetGenerator(); return ( ); } ``` <1> [Implementation details](#implementing-facet-components) follow in the next section. <2> Get the current list of `facets` array returned by the facet generator and render the appropriate component based on the facet `type`. Next, let's look at how to implement the different types of facet components. ### Implementing facet components As an example, we'll now look at how to implement the `Regular` facet component. Implementation details for the other types of facets can be found in the [sample project in the Headless repository](https://github.com/coveo/ui-kit/tree/main/samples/headless-ssr/commerce-nextjs/components/facets). ```tsx // components/facets/regular-facet.tsx 'use client'; import { RegularFacet as HeadlessRegularFacet, RegularFacetState, RegularFacetValue, } from '@coveo/headless-react/ssr-commerce'; import {useEffect, useRef, useState} from 'react'; interface IRegularFacetProps { <1> controller?: HeadlessRegularFacet; staticState: RegularFacetState; } export default function RegularFacet(props: IRegularFacetProps) { const {controller, staticState} = props; const [state, setState] = useState(staticState); <2> useEffect(() => { controller?.subscribe(() => setState(controller.state)); }, [controller]); <3> const onClickClearSelectedFacetValues = (): void => { controller?.deselectAll(); }; const onChangeFacetValue = (facetValue: RegularFacetValue): void => { controller?.toggleSelect(facetValue); }; const renderFacetValues = () => { <4> return (
{state.isLoading && ( Facet is loading... )}
); }; return (
{state.displayName ?? state.facetId} {renderFacetValues()}
); } ``` <1> Create an interface for the props passed down by the `FacetGenerator` component, namely the Headless facet controller and its static state. <2> Create a state variable for your component state. You'll have to keep it synchronized with the state of the Headless facet controller. <3> Create a `useEffect` hook to subscribe to changes in the headless facet controller state and update your facet component accordingly. <4> Create a `renderFacetValues` function to render the facet values to the UI. <5> Iterate through each `value` in the `state.values` array and render the checkbox, label, and number of results. <6> Render the checkbox input element and bind the `onChangeFacetValue` method to the `onChange` event. <7> Create buttons for various user functionalities. When a user clicks the buttons, call the corresponding method on the controller to interact with the facet. ## Sorting Sorting lets users order results based on certain criteria like relevance, popularity, or price. The following code snippet demonstrates how to implement sorting in your commerce SSR interface. > **Important** > > Metadata keys defined in [variant](https://docs.coveo.com/en/m53g0506/) and [availability](https://docs.coveo.com/en/m53g0124/) data can be used for filtering with [facets](https://docs.coveo.com/en/198/), but can't be used for sorting results. ```tsx 'use client'; import {useSort} from '@/lib/commerce-engine'; import {SortBy, SortCriterion} from '@coveo/headless-react/ssr-commerce'; export default function Sort() { const {state, methods} = useSort(); if (state.availableSorts.length === 0) { return null; } const formatSortFieldLabel = (field: { name: string; direction?: string; displayName?: string; }) => field?.displayName ?? `${field.name} ${field.direction ?? ''}`.trim(); const getSortLabel = (criterion: SortCriterion) => { <1> switch (criterion.by) { case SortBy.Relevance: return 'Relevance'; case SortBy.Fields: return criterion.fields.map(formatSortFieldLabel); } }; return (
); } ``` <1> Create a `getSortLabel` function that returns the label for the sort criterion based on its type. If the current results are sorted by relevance, return `Relevance`. Otherwise, stringify the fields and return the value. <2> Use a `select` element to display the available sort options, utilizing the string representation of the applied sort as the value. When the user selects a new sort criterion, call the [`sortBy`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.Sort.html#sortby-1) method on the controller to sort the results accordingly. <3> Iterate through the [`availableSorts`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.SortState.html#appliedsort) and render an `option` element for each sort criterion. When the user selects a sort criterion, call the `sortBy` method on the controller to sort the results by the selected criterion. ## Pagination Pagination breaks large sets of results into smaller, manageable pages, improving navigation. The following code snippet demonstrates how to implement pagination in your commerce SSR interface. ```tsx 'use client'; import {usePagination} from '@/lib/commerce-engine'; export default function Pagination() { const {state, methods} = usePagination(); const renderPageRadioButtons = () => { <1> return Array.from({length: state.totalPages}, (_, i) => { const page = i + 1; return ( ); }); }; return (
Page {state.page + 1} of {state.totalPages}
{renderPageRadioButtons()}
); } ``` <1> Create a `renderPageRadioButtons` function to render radio buttons for each page. When the user selects a page, call the `selectPage` method on the controller to navigate to the selected page. <2> Display buttons to navigate to the previous and next pages. When the user clicks the buttons, call the [`previousPage`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.Pagination.html#previouspage) and [`nextPage`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.Pagination.html#nextpage) methods on the controller to navigate to the previous and next pages, respectively.