UI libraries / React InstantSearch / Widgets

This is the React InstantSearch v7 documentation. React InstantSearch v7 is the latest version of React InstantSearch and the stable version of React InstantSearch Hooks.

If you were using React InstantSearch v6, you can upgrade to v7.

If you were using React InstantSearch Hooks, you can still use the React InstantSearch v7 documentation, but you should check the upgrade guide for necessary changes.

If you want to keep using React InstantSearch v6, you can find the archived documentation.

Signature
<HierarchicalMenu
  attributes={string[]}
  // Optional props
  limit={number}
  showMore={boolean}
  showMoreLimit={number}
  separator={string}
  rootPath={string}
  showParentLevel={boolean}
  sortBy={string[] | function}
  transformItems={function}
  classNames={object}
  translations={object}
  ...props={ComponentProps<'div'>}
/>
Import
1
import { HierarchicalMenu } from 'react-instantsearch';

About this widget

<HierarchicalMenu> is a widget that creates a navigation menu based on a hierarchy of facet attributes. It’s commonly used for categories with subcategories.

By default, the count of the refined root level is updated to match the count of the actively refined parent level. You can choose to keep the root level count intact by setting persistHierarchicalRootCount in <InstantSearch>.

Requirements

The objects to use in the hierarchical menu must follow this structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[
  {
    "objectID": "321432",
    "name": "lemon",
    "categories.lvl0": "products",
    "categories.lvl1": "products > fruits"
  },
  {
    "objectID": "8976987",
    "name": "orange",
    "categories.lvl0": "products",
    "categories.lvl1": "products > fruits"
  }
]

You can also provide more than one path for each level:

1
2
3
4
5
6
7
8
[
  {
    "objectID": "321432",
    "name": "lemon",
    "categories.lvl0": ["products", "goods"],
    "categories.lvl1": ["products > fruits", "goods > to eat"]
  }
]

The attributes provided to the widget must be in attributes for faceting, either on the dashboard) or using attributesForFaceting with the API. By default, the separator is > (with spaces), but you can use a different one by using the separator option.

You can also create your own UI with useHierarchicalMenu().

Examples

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React from 'react';
import algoliasearch from 'algoliasearch/lite';
import { InstantSearch, HierarchicalMenu } from 'react-instantsearch';

const searchClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');

function App() {
  return (
    <InstantSearch indexName="instant_search" searchClient={searchClient}>
      <HierarchicalMenu
        attributes={[
          'categories.lvl0',
          'categories.lvl1',
          'categories.lvl2',
          'categories.lvl3',
        ]}
      />
    </InstantSearch>
  );
}

Props

Parameter Description
attributes
type: string[]
Required

The name of the attributes to generate the menu with. To avoid unexpected behavior, you can’t use the same attribute prop in a different type of widget.

1
2
3
4
5
6
7
8
<HierarchicalMenu
  attributes={[
    'categories.lvl0',
    'categories.lvl1',
    'categories.lvl2',
    'categories.lvl3',
  ]}
/>
limit
type: number
default: 10

The number of facet values to retrieve. When you enable the showMore feature, this is the number of facet values to display before clicking the “Show more” button.

1
2
3
4
<HierarchicalMenu
  // ...
  limit={5}
/>
showMore
type: boolean
default: false

Whether to display a button that expands the number of items.

1
2
3
4
<HierarchicalMenu
  // ...
  showMore={true}
/>
showMoreLimit
type: number

The maximum number of displayed items (only used when showMore is set to true).

1
2
3
4
<HierarchicalMenu
  // ...
  showMoreLimit={20}
/>
separator
type: string
default: " > "

The level separator used in the records.

1
2
3
4
<HierarchicalMenu
  // ...
  separator=" / "
/>
rootPath
type: string

The prefix path to use if the first level isn’t the root level.

Make sure to also include the root path in your UI state—for example, by setting initialUiState or calling setUiState().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<InstantSearch
  // ...
  initialUiState={{
    YourIndexName: {
      hierarchicalMenu: {
        'categories.lvl0': ['Computers & Tablets'],
      },
    },
  }}
>
  <HierarchicalMenu
    // ...
    rootPath="Computers & Tablets"
  />
</InstantSearch>
showParentLevel
type: boolean
default: true

Whether to show the siblings of the selected parent level of the current refined value.

This option doesn’t impact the root level. All root items are always visible.

1
2
3
4
<HierarchicalMenu
  // ...
  showParentLevel={false}
/>
sortBy
type: string[] | (a: FacetValue, b: FacetValue) => number
default: ["name:asc"], or `facetOrdering` if set

How to sort refinements. Must be one or more of the following strings:

  • "count" (same as "count:desc")
  • "count:asc"
  • "count:desc"
  • "name" (same as "name:asc")
  • "name:asc"
  • "name:desc"
  • "isRefined" (same as "isRefined:asc")
  • "isRefined:asc"
  • "isRefined:desc"

You can also give a function that receives items two by two, like JavaScript’s Array.sort.

1
2
3
4
<HierarchicalMenu
  // ...
  sortBy={['isRefined']}
/>
transformItems
type: (items: object[], metadata: { results: SearchResults }) => object[]

Receives the items and is called before displaying them. Should return a new array with the same shape as the original array. This is helpful when transforming, removing, or reordering items.

In addition, the full results data is available, which includes all regular response parameters, as well as parameters from the helper (for example disjunctiveFacetsRefinements).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const transformItems = (items) => {
  return items.map((item) => ({
    ...item,
    label: item.label.toUpperCase(),
  }));
};

function Search() {
  return (
    <HierarchicalMenu
      // ...
      transformItems={transformItems}
    />
  );
}
classNames
type: Partial<HierarchicalMenuClassNames>

The CSS classes you can override and pass to the widget’s elements. It’s useful to style widgets with class-based CSS frameworks like Bootstrap or Tailwind CSS.

  • root: The root element of the widget.
  • noRefinementRoot: The root element when there are no refinements.
  • list: The list element.
  • item: Each item element.
  • selectedItem: The selected item element.
  • parentItem: The parent item of the list.
  • link: The link of each item.
  • selectedItemLink: The link of each selected item element.
  • label: The label of each item.
  • count: The count of each item.
  • showMore: The “Show more” button.
  • disabledShowMore: The disabled “Show more” button.
1
2
3
4
5
6
7
<HierarchicalMenu
  // ...
  classNames={{
    root: 'MyCustomHierarchicalMenu',
    list: 'MyCustomHierarchicalMenuList MyCustomHierarchicalMenuList--subclass',
  }}
/>
translations
type: Partial<HierarchicalMenuTranslations>

A dictionary of translations to customize the UI text and support internationalization.

  • showMoreButtonText: The text for the “Show more” button.
1
2
3
4
5
6
7
8
9
<HierarchicalMenu
  // ...
  showMore
  translations={{
    showMoreButtonText({ isShowingMore }) { 
      return isShowingMore ? 'Show less brands' : 'Show more brands';
    },
  }}
/>
...props
type: React.ComponentProps<'div'>

Any <div> prop to forward to the widget’s root element.

1
<HierarchicalMenu className="MyCustomHierarchicalMenu" title="My custom title" />

Hook

React InstantSearch let you create your own UI for the <HierarchicalMenu> widget with useHierarchicalMenu(). Hooks provide APIs to access the widget state and interact with InstantSearch.

The useHierarchicalMenu() Hook accepts parameters and returns APIs.

Usage

First, create your React component:

import { useHierarchicalMenu } from 'react-instantsearch';
  
function CustomHierarchicalMenu(props) {
  const {
    items,
    isShowingMore,
    canToggleShowMore,
    canRefine,
    refine,
    sendEvent,
    toggleShowMore,
    createURL,
  } = useHierarchicalMenu(props);

  return <>{/* Your JSX */}</>;
}

Then, render the widget:

<CustomHierarchicalMenu {...props} />

Parameters

Hooks accept parameters. You can pass them manually, or forward the props from your custom component.

When you provide a function to Hooks, make sure to pass a stable reference to avoid rendering endlessly (for example, with useCallback()). Objects and arrays are memoized; you don’t need to stabilize them.

Parameter Description
attributes
type: string[]
Required

The names of the attributes inside the records.

To avoid unexpected behavior, you can’t use the same attribute prop in a different type of widget.

1
2
3
4
5
6
7
8
const hierarchicalMenuApi = useHierarchicalMenu({
  attributes: [
    'categories.lvl0',
    'categories.lvl1',
    'categories.lvl2',
    'categories.lvl3',
  ],
});
separator
type: string
default: " > "

The level separator used in the records.

1
2
3
4
const hierarchicalMenuApi = useHierarchicalMenu({
  // ...
  separator: ' - ',
});
rootPath
type: string

The path to use if the first level isn’t the root level.

Make sure to also include the root path in your UI state—for example, by setting initialUiState or calling setUiState().

1
2
3
4
const hierarchicalMenuApi = useHierarchicalMenu({
  // ...
  rootPath: 'Audio > Home Audio',
});
showParentLevel
type: boolean
default: true

Whether to show the siblings of the selected parent level of the current refined value.

1
2
3
4
const hierarchicalMenuApi = useHierarchicalMenu({
  // ...
  showParentLevel: false,
});
limit
type: number
default: 10

How many facet values to retrieve.

When you enable the showMore feature, this is the number of facet values to display before clicking the Show more button.

1
2
3
4
const hierarchicalMenuApi = useHierarchicalMenu({
  // ...
  limit: 5,
});
showMore
type: boolean
default: false

Whether to display a button that expands the number of items.

1
2
3
4
const hierarchicalMenuApi = useHierarchicalMenu({
  // ...
  showMore: true,
});
showMoreLimit
type: number

The maximum number of displayed items. Only used when showMore is set to true.

1
2
3
4
const hierarchicalMenuApi = useHierarchicalMenu({
  // ...
  showMoreLimit: 30,
});
sortBy
type: string[] | (a: RefinementListItem, b: RefinementListItem) => number
default: ["name:asc"] (or `facetOrdering` if set)

How to sort refinements. Must be one or more of the following strings:

  • "count" (same as "count:desc")
  • "count:asc"
  • "count:desc"
  • "name" (same as "name:asc")
  • "name:asc"
  • "name:desc"
  • "isRefined" (same as "isRefined:asc")
  • "isRefined:asc"
  • "isRefined:desc"

You can also use a sort function that behaves like the standard Javascript compareFunction.

When you don’t set this parameter, and you’ve set facetOrdering for this facet in renderingContent, facets are sorted using facetOrdering and use the default order as a fallback.

1
2
3
4
const hierarchicalMenuApi = useHierarchicalMenu({
  // ...
  sortBy: ['count', 'name:asc'],
});
transformItems
type: (items: object[], metadata: { results: SearchResults }) => object[]

Receives the items and is called before displaying them. Should return a new array with the same shape as the original array. This is helpful when transforming, removing, or reordering items.

In addition, the full results data is available, which includes all regular response parameters, as well as parameters from the helper (for example disjunctiveFacetsRefinements).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const transformItems = (items) => {
  return items.map((item) => ({
    ...item,
    label: item.label.toUpperCase(),
  }));
};

function HierarchicalMenu() {
  const hierarchicalMenuApi = useHierarchicalMenu({
    // ...
    transformItems,
  });

  return <>{/* Your JSX */}</>;
}

APIs

Hooks return APIs, such as state and functions. You can use them to build your UI and interact with React InstantSearch.

Parameter Description
items
type: HierarchicalMenuItem[]

The list of items the widget can display.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type HierarchicalMenuItem = {
  /**
  * Value of the menu item.
  */
  value: string;
  /**
  * Human-readable value of the menu item.
  */
  label: string;
  /**
  * Number of matched results after refinement is applied.
  */
  count: number;
  /**
  * Indicates if the refinement is applied.
  */
  isRefined: boolean;
  /**
  * n+1 level of items
  */
  data: HierarchicalMenuItem[] | null;
};
isShowingMore
type: boolean

Whether the list is expanded.

canToggleShowMore
type: boolean

Whether users can click the “Show more” button.

canRefine
type: boolean

Indicates if you can refine the search state.

refine
type: (value: string) => void

Sets the path of the hierarchical filter and triggers a new search.

sendEvent
type: (eventType: string, facetValue: string, eventName?: string) => void

The function to send click events.

  • The view event is automatically sent when the facets are rendered.
  • The click event is automatically sent when refine is called.
  • You can learn more about the insights middleware.

  • eventType: 'click'
  • facetValue: string
toggleShowMore
type: () => void

Toggles the number of displayed values between limit and showMoreLimit.

createURL
type: (value: string) => string

Generates a URL for the next state.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import React from 'react';
import { useHierarchicalMenu } from 'react-instantsearch';

function CustomHierarchicalMenu(props) {
  const {
  items,
  refine,
  canToggleShowMore,
  toggleShowMore,
  isShowingMore,
  createURL,
} = useHierarchicalMenu(props);

  return (
    <>
      <HierarchicalList
        items={items}
        onNavigate={refine}
        createURL={createURL}
      />
      {props.showMore && (
        <button disabled={!canToggleShowMore} onClick={toggleShowMore}>
          {isShowingMore ? 'Show less' : 'Show more'}
        </button>
      )}
    </>
  );
}

function HierarchicalList({ items, createURL, onNavigate }) {
  if (items.length === 0) {
    return null;
  }

  return (
    <ul>
      {items.map((item) => (
        <li key={item.value}>
          <a
            href={createURL(item.value)}
            onClick={(event) => {
              if (isModifierClick(event)) {
                return;
              }
              event.preventDefault();

              onNavigate(item.value);
            }}
            style={{ fontWeight: item.isRefined ? 'bold' : 'normal' }}
          >
            <span>{item.label}</span>
            <span>{item.count}</span>
          </a>
          {item.data && (
            <HierarchicalList
              items={item.data}
              onNavigate={onNavigate}
              createURL={createURL}
            />
          )}
        </li>
      ))}
    </ul>
  );
}
Did you find this page helpful?