---
title: Leveraging facets, sorting, and pagination (CSR)
slug: o7vb0270
canonical_url: https://docs.coveo.com/en/o7vb0270/
collection: coveo-for-commerce
source_format: adoc
---
# Leveraging facets, sorting, and pagination (CSR)
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.
Headless provides a way to use these features through sub-controllers.
> **Note**
>
> In the following sections, you'll notice that the Headless front-end 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
## Using the sub-controllers
Common functionalities like facets, sorting, and pagination are implemented using sub-controllers in Headless.
Depending on the solution you're implementing—whether it's Search, Listing, or Recommendations—you can use the appropriate controller to access the sub-controller you need:
* [`Search`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.Search.html)
* [`ProductListing`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.ProductListing.html)
* [`Recommendations`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.Recommendations.html)
To access the sub-controllers, you first need to build the parent controller.
**Example**
The following example showcases how to use the `Search` controller to access the `Sort` sub-controller:
```ts
import { buildSearch } from '@coveo/headless/commerce';
const searchController = buildSearch(engine); <1>
const searchSortSubController = searchController.sort(); <2>
```
<1> Build the `Search` controller by passing in the previously initialized engine to the `buildSearch` method.
<2> Access the `Sort` sub-controller by calling the `sort` method on the parent controller.
The sections in the remainder of this article assume the relevant controller has been built and the sub-controller has been accessed.
The code samples are agnostic to whether the sub-controller is accessed through the `Search`, `Listing`, or `Recommendations` controller.
For a complete example of how to build a controller and access sub-controllers, see the usage of the `Search` controller in this [sample project in the Headless repository](https://github.com/coveo/ui-kit/tree/master/packages/samples/headless-commerce-react/src/pages/search-page.tsx).
## 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
Facets are accessed using the `FacetGenerator` sub-controller on your [`Search`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.Search.html#facetGenerator) or [`ProductListing`](https://docs.coveo.com/en/headless/latest/reference/interfaces/Commerce.ProductListing.html#facetGenerator) controller.
This sub-controller provides a list of facets that can be rendered in the UI.
There are five 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:
```jsx
import { FacetGenerator as HeadlessFacetGenerator } from '@coveo/headless/commerce';
import { useEffect, useState } from 'react';
import CategoryFacet from '../category-facet/category-facet';
import DateFacet from '../date-facet/date-facet';
import NumericFacet from '../numeric-facet/numeric-facet';
import RegularFacet from '../regular-facet/regular-facet';
interface IFacetGeneratorProps {
controller: HeadlessFacetGenerator;
}
export default function FacetGenerator(props: IFacetGeneratorProps) {
const {controller} = props;
const [facetState, setFacetState] = useState(controller.facets);
useEffect(() => { <1>
controller.subscribe(() => {
setFacetState(controller.facets);
});
}, [controller]);
if (facetState.length === 0) {
return null;
}
return (
{facetState.map((facet, index) => { <2>
switch (facet.type) {
case 'regular':
return ;
case 'numericalRange':
return ;
case 'hierarchical':
return ;
case 'dateRange':
return ;
default:
return null;
}
})}
);
}
```
<1> Subscribe to the controller to update the state when the facets change.
<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
[sample project in the Headless repository](https://github.com/coveo/ui-kit/tree/main/samples/headless/commerce-react/src/components/facets).
```jsx
import {RegularFacet as HeadlessRegularFacet} from '@coveo/headless/commerce';
import {useEffect, useState} from 'react';
interface IRegularFacetProps {
controller: HeadlessRegularFacet;
}
export default function RegularFacet(props: IRegularFacetProps) {
const {controller} = props;
const [state, setState] = useState(controller.state);
useEffect(() => {
controller.subscribe(() => setState(controller.state));
}, [controller]);
const renderFacetValues = () => { <1>
return (
);
}
```
<1> Create a `renderFacetValues` function to render the facet values to the UI.
<2> Iterate through each `value` in the `state.values` array and render the checkbox, label, and number of results.
If the user selects a value, call the `toggleSelect` method on the controller to select or deselect it.
<3> Render the checkbox input element and bind the `toggleSelect` method to the `onChange` event.
Additionally, if the value is selected, set the `checked` attribute to `true`.
<4> 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.
> **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.
```jsx
import {
Sort as HeadlessSort,
SortBy,
SortCriterion,
} from '@coveo/headless/commerce';
import {useEffect, useState} from 'react';
interface ISortProps {
controller: HeadlessSort;
}
export default function Sort(props: ISortProps) {
const {controller} = props;
const [state, setState] = useState(controller.state);
useEffect(() => {
controller.subscribe(() => setState(controller.state));
}, [controller]);
if (state.availableSorts.length === 0) {
return null;
}
const getSortLabel = (criterion: SortCriterion) => { <1>
switch (criterion.by) {
case SortBy.Relevance:
return 'Relevance';
case SortBy.Fields:
return criterion.fields.map((field) => field.displayName).join(', ');
}
};
return (
);
}
```
<1> Create a `getSortLabel` function to return the label for the sort criterion based on its type.
If 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, using the string representation of the applied sort as the value.
When the user selects a new sort criterion, call the `sortBy` method on the controller to sort the results by the selected criterion.
<3> Iterate through the `availableSorts` 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.
```jsx
import {Pagination as HeadlessPagination} from '@coveo/headless/commerce';
import {useEffect, useState} from 'react';
interface IPaginationProps {
controller: HeadlessPagination;
}
export default function Pagination(props: IPaginationProps) {
const {controller} = props;
const [state, setState] = useState(controller.state);
useEffect(() => {
controller.subscribe(() => setState(controller.state));
}, [controller]);
const renderPageRadioButtons = () => { <1>
return Array.from({length: state.totalPages}, (_, i) => {
const page = i + 1;
return (
);
});
};
return (
{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` and `nextPage` methods on the controller to navigate to the previous and next pages, respectively.