> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# HierarchicalMenu

> Shows a hierarchical menu to navigate nested categories.

export const Records = () => <Tooltip tip="A record is a searchable object in an Algolia index. Each record consists of named attributes." cta="Algolia records" href="/doc/guides/sending-and-managing-data/prepare-your-data#algolia-records">
    records
  </Tooltip>;

<Note>
  This is the **React InstantSearch v7** documentation.
  If you're upgrading from v6, see the [upgrade guide](/doc/guides/building-search-ui/upgrade-guides/react/#migrate-from-react-instantsearch-v6-to-react-instantsearch-v7).
  If you were using React InstantSearch Hooks,
  this v7 documentation applies—just check for [necessary changes](/doc/guides/building-search-ui/upgrade-guides/react/#migrate-from-react-instantsearch-hooks-to-react-instantsearch-v7).
  To continue using v6, you can find the [archived documentation](https://algolia.com/old-docs/deprecated/instantsearch/react/v6/api-reference/instantsearch/).
</Note>

```tsx Signature theme={"system"}
<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

```js JavaScript icon=code theme={"system"}
import { HierarchicalMenu } from "react-instantsearch";
```

<Card title="See this widget in action" icon="monitor-play" href="https://instantsearchjs.netlify.app/stories/?path=/story/refinements-HierarchicalMenu--default" horizontal>
  Preview this widget and its behavior.
</Card>

## About this widget

The `<HierarchicalMenu>` widget displays a hierarchical navigation menu, based on facet attributes.

To create a hierarchical menu:

1. Decide on an appropriate [facet hierarchy](/doc/guides/managing-results/refine-results/faceting#hierarchical-facets)
2. Determine your [`attributes`](#param-attributes) for faceting from the [dashboard](/doc/guides/managing-results/refine-results/faceting/how-to/declaring-attributes-for-faceting-with-dashboard) or with an [API client](/doc/guides/managing-results/refine-results/faceting/how-to/declaring-attributes-for-faceting)
3. Display the UI with the hierarchical menu widget.

To learn more, see [Facet display](/doc/guides/building-search-ui/ui-and-ux-patterns/facet-display/react).

<Tip>You can also create your own UI with [`useHierarchicalMenu`](#hook).</Tip>

### Requirements

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

```json JSON icon=braces theme={"system"}
[
  {
    "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:

```json JSON icon=braces theme={"system"}
[
  {
    "objectID": "321432",
    "name": "lemon",
    "categories.lvl0": ["products", "goods"],
    "categories.lvl1": ["products > fruits", "goods > to eat"]
  }
]
```

By default, the separator is `>` (with spaces), but you can use a different one by using the [separator](#param-separator) option.

<Note>
  By default, the count of the refined root level is updated to match the count of the actively refined parent level.
  Keep the root level count intact by setting [`persistHierarchicalRootCount`](/doc/api-reference/widgets/instantsearch/react#param-future-persist-hierarchical-root-count) in [`<InstantSearch>`](/doc/api-reference/widgets/instantsearch/react).
</Note>

## Examples

```jsx JavaScript icon=code theme={"system"}
import React from "react";
import { liteClient as 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

<ParamField body="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.

  ```jsx JavaScript icon=code theme={"system"}
  <HierarchicalMenu
    attributes={[
      "categories.lvl0",
      "categories.lvl1",
      "categories.lvl2",
      "categories.lvl3",
    ]}
  />;
  ```
</ParamField>

<ParamField body="limit" type="number" default={10}>
  The number of facet values to retrieve.
  When you enable the [`showMore`](#param-show-more) feature,
  this is the number of facet values to display before clicking the "Show more" button.

  ```jsx JavaScript icon=code theme={"system"}
  <HierarchicalMenu
    // ...
    limit={5}
  />;
  ```
</ParamField>

<ParamField body="showMore" type="boolean" default={false}>
  Whether to display a button that expands the number of items.

  ```jsx JavaScript icon=code theme={"system"}
  <HierarchicalMenu
    // ...
    showMore={true}
  />;
  ```
</ParamField>

<ParamField body="showMoreLimit" type="number">
  The maximum number of displayed items (only used when [`showMore`](#param-show-more) is set to `true`).

  ```jsx JavaScript icon=code theme={"system"}
  <HierarchicalMenu
    // ...
    showMoreLimit={20}
  />;
  ```
</ParamField>

<ParamField body="separator" type="string" default="">
  The level separator used in the <Records />.

  ```jsx JavaScript icon=code theme={"system"}
  <HierarchicalMenu
    // ...
    separator=" / "
  />;
  ```
</ParamField>

<ParamField body="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](/doc/api-reference/widgets/ui-state/react),
  for example, by setting [`initialUiState`](/doc/api-reference/widgets/instantsearch/react#param-initial-ui-state) or calling [`setUiState`](/doc/api-reference/widgets/use-instantsearch/react#param-set-ui-state).

  ```jsx JavaScript icon=code theme={"system"}
  <InstantSearch
  // ...
  initialUiState={{
  YourInexName: {
    hierarchicalMenu: {
      "categories.lvl0": ["Computers & Tablets"],
    },
  },
  }}
  >
  <HierarchicalMenu
  // ...
  rootPath="Computers & Tablets"
  />
  </InstantSearch>;

  ```
</ParamField>

<ParamField body="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.

  ```jsx JavaScript icon=code theme={"system"}
  <HierarchicalMenu
    // ...
    showParentLevel={false}
  />;
  ```
</ParamField>

<ParamField body="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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).

  <CodeGroup>
    ```jsx String theme={"system"}
    <HierarchicalMenu
      // ...
      sortBy={["isRefined"]}
    />;
    ```

    ```jsx Function theme={"system"}
    const sortByName = (a, b) => {
      return a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase() ? -1 : 1;
    };

    function Search() {
      return (
        <HierarchicalMenu
          // ...
          sortBy={sortByName}
        />
      );
    }
    ```

    ```tsx Function (TypeScript) theme={"system"}
    const sortByName: HierarchicalMenuProps["sortBy"] = (a, b) => {
      return a.name.toLocaleLowerCase() < b.name.toLocaleLowerCase() ? -1 : 1;
    };

    function Search() {
      return (
        <HierarchicalMenu
          // ...
          sortBy={sortByName}
        />
      );
    }
    ```
  </CodeGroup>
</ParamField>

<ParamField body="transformItems" type="(items: object[], metadata: { results: SearchResults }) => object[]">
  A function that receives the list of items before they are displayed.
  It should return a new array with the same structure.
  Use this to transform, filter, or reorder the items.

  The function also has access to the full `results` data,
  including all standard [response parameters](/doc/guides/building-search-ui/going-further/backend-search/in-depth/understanding-the-api-response/)
  and [parameters from the helper](https://community.algolia.com/algoliasearch-helper-js/reference.html#query-parameters),
  such as `disjunctiveFacetsRefinements`.

  <CodeGroup>
    ```jsx JavaScript theme={"system"}
    const transformItems = (items) => {
      return items.map((item) => ({
        ...item,
        label: item.label.toUpperCase(),
      }));
    };

    function Search() {
      return (
        <HierarchicalMenu
          // ...
          transformItems={transformItems}
        />
      );
    }
    ```

    ```tsx TypeScript theme={"system"}
    import type { HierarchicalMenuProps } from "react-instantsearch";

    const transformItems: HierarchicalMenuProps["transformItems"] = (items) => {
      return items.map((item) => ({
        ...item,
        label: item.label.toUpperCase(),
      }));
    };

    function Search() {
      return (
        <HierarchicalMenu
          // ...
          transformItems={transformItems}
        />
      );
    }
    ```
  </CodeGroup>
</ParamField>

<ParamField body="classNames" type="Partial<HierarchicalMenuClassNames>">
  The [CSS classes you can override](/doc/guides/building-search-ui/widgets/customize-an-existing-widget/react#style-your-widgets) and pass to the widget's elements.
  It's useful to style widgets with class-based CSS frameworks like [Bootstrap](https://getbootstrap.com) or [Tailwind CSS](https://tailwindcss.com).

  * `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.

  ```jsx JavaScript icon=code theme={"system"}
  <HierarchicalMenu
    // ...
    classNames={{
      root: "MyCustomHierarchicalMenu",
      list: "MyCustomHierarchicalMenuList MyCustomHierarchicalMenuList--subclass",
    }}
  />;
  ```
</ParamField>

<ParamField body="translations" type="Partial<HierarchicalMenuTranslations>">
  A dictionary of translations to customize the UI text and support internationalization.

  * `showMoreButtonText`. The text for the "Show more" button.

  ```jsx JavaScript icon=code theme={"system"}
  <HierarchicalMenu
    // ...
    showMore
    translations={{
      showMoreButtonText({ isShowingMore }) {
        return isShowingMore ? "Show less brands" : "Show more brands";
      },
    }}
  />;
  ```
</ParamField>

<ParamField body="...props" type="React.ComponentProps<'div'>">
  Any `<div>` prop to forward to the widget's root element.

  ```jsx JavaScript icon=code theme={"system"}
  <HierarchicalMenu
    className="MyCustomHierarchicalMenu"
    title="My custom title"
  />;
  ```
</ParamField>

## 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](#parameters) and returns [APIs](#hook).
It must be used inside the [`<InstantSearch>`](/doc/api-reference/widgets/instantsearch/react) component.

### Usage

First, create your React component:

```jsx JavaScript icon=code theme={"system"}
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:

```jsx JavaScript icon=code theme={"system"}
<CustomHierarchicalMenu {...props} />;
```

### Parameters

Hooks accept parameters. You can either pass them manually or forward props from a custom component.

<Note>
  When passing functions to Hooks, ensure stable references to prevent unnecessary re-renders.
  Use [`useCallback()`](https://reactjs.org/docs/hooks-reference.html#usecallback) for memoization.
  Arrays and objects are automatically memoized.
</Note>

<ParamField body="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.

  ```jsx JavaScript icon=code theme={"system"}
  const hierarchicalMenuApi = useHierarchicalMenu({
    attributes: [
      "categories.lvl0",
      "categories.lvl1",
      "categories.lvl2",
      "categories.lvl3",
    ],
  });
  ```
</ParamField>

<ParamField body="separator" type="string" default="">
  The level separator used in the records.

  ```jsx JavaScript icon=code theme={"system"}
  const hierarchicalMenuApi = useHierarchicalMenu({
    // ...
    separator: " - ",
  });
  ```
</ParamField>

<ParamField body="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](/doc/api-reference/widgets/ui-state/react),
  for example, by setting [`initialUiState`](/doc/api-reference/widgets/instantsearch/react#param-initial-ui-state) or calling [`setUiState`](/doc/api-reference/widgets/use-instantsearch/react#param-set-ui-state).

  ```jsx JavaScript icon=code theme={"system"}
  const hierarchicalMenuApi = useHierarchicalMenu({
    // ...
    rootPath: "Audio > Home Audio",
  });
  ```
</ParamField>

<ParamField body="showParentLevel" type="boolean" default={true}>
  Whether to show the siblings of the selected parent level of the current refined value.

  ```jsx JavaScript icon=code theme={"system"}
  const hierarchicalMenuApi = useHierarchicalMenu({
    // ...
    showParentLevel: false,
  });
  ```
</ParamField>

<ParamField body="limit" type="number" default={10}>
  How many facet values to retrieve.

  When you enable the [`showMore`](#param-show-more) feature,
  this is the number of facet values to display before clicking the Show more button.

  ```jsx JavaScript icon=code theme={"system"}
  const hierarchicalMenuApi = useHierarchicalMenu({
    // ...
    limit: 5,
  });
  ```
</ParamField>

<ParamField body="showMore" type="boolean" default={false}>
  Whether to display a button that expands the number of items.

  ```jsx JavaScript icon=code theme={"system"}
  const hierarchicalMenuApi = useHierarchicalMenu({
    // ...
    showMore: true,
  });
  ```
</ParamField>

<ParamField body="showMoreLimit" type="number">
  The maximum number of displayed items.
  Only used when [`showMore`](#param-show-more) is set to `true`.

  ```jsx JavaScript icon=code theme={"system"}
  const hierarchicalMenuApi = useHierarchicalMenu({
    // ...
    showMoreLimit: 30,
  });
  ```
</ParamField>

<ParamField body="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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Syntax).

  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.

  ```jsx JavaScript icon=code theme={"system"}
  const hierarchicalMenuApi = useHierarchicalMenu({
    // ...
    sortBy: ["count", "name:asc"],
  });
  ```
</ParamField>

<ParamField body="transformItems" type="(items: object[], metadata: { results: SearchResults }) => object[]">
  A function that receives the list of items before they are displayed.
  It should return a new array with the same structure.
  Use this to transform, filter, or reorder the items.

  The function also has access to the full `results` data,
  including all standard [response parameters](/doc/guides/building-search-ui/going-further/backend-search/in-depth/understanding-the-api-response/)
  and [parameters from the helper](https://community.algolia.com/algoliasearch-helper-js/reference.html#query-parameters),
  such as `disjunctiveFacetsRefinements`.

  <CodeGroup>
    ```jsx JavaScript theme={"system"}
    const transformItems = (items) => {
      return items.map((item) => ({
        ...item,
        label: item.label.toUpperCase(),
      }));
    };

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

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

    ```tsx TypeScript theme={"system"}
    import type { UseHierarchicalMenuProps } from 'react-instantsearch';

    const transformItems: UseHierarchicalMenuProps['transformItems'] = (items) => {
      return items.map((item) => ({
        ...item,
        label: item.label.toUpperCase(),
      }));
    };

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

      return <>{/* Your JSX */}</>;
    }
    ```
  </CodeGroup>
</ParamField>

### APIs

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

<ParamField body="items" type="HierarchicalMenuItem[]">
  The list of items the widget can display.

  ```ts TypeScript icon=code theme={"system"}
  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;
  };
  ```
</ParamField>

<ParamField body="isShowingMore" type="boolean">
  Whether the list is expanded.
</ParamField>

<ParamField body="canToggleShowMore" type="boolean">
  Whether users can click the "Show more" button.
</ParamField>

<ParamField body="canRefine" type="boolean">
  Indicates if you can refine the search state.
</ParamField>

<ParamField body="refine" type="(value: string) => void">
  Sets the path of the hierarchical filter and triggers a new search.
</ParamField>

<ParamField body="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`](/doc/api-reference/widgets/insights/react) middleware.
  * `eventType: 'click'`
  * `facetValue: string`
</ParamField>

<ParamField body="toggleShowMore" type="() => void">
  Toggles the number of displayed values between [`limit`](/doc/api-reference/widgets/hierarchical-menu/react#param-limit)
  and [`showMoreLimit`](/doc/api-reference/widgets/hierarchical-menu/react#param-show-more-limit).
</ParamField>

<ParamField body="createURL" type="(value: string) => string">
  Generates a URL for the next state.
</ParamField>

### Example

<CodeGroup>
  ```jsx JavaScript theme={"system"}
  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>
    );
  }

  function isModifierClick(event) {
    const isMiddleClick = event.button === 1;
    return Boolean(
      isMiddleClick ||
        event.altKey ||
        event.ctrlKey ||
        event.metaKey ||
        event.shiftKey,
    );
  }
  ```

  ```tsx TypeScript theme={"system"}
  import React from "react";
  import {
    useHierarchicalMenu,
    UseHierarchicalMenuProps,
  } from "react-instantsearch";

  function CustomHierarchicalMenu(props: UseHierarchicalMenuProps) {
    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>
        )}
      </>
    );
  }

  type HierarchicalListProps = Pick<
    ReturnType<typeof useHierarchicalMenu>,
    "items" | "createURL"
  > & {
    onNavigate(value: string): void;
  };

  function HierarchicalList({
    items,
    createURL,
    onNavigate,
  }: HierarchicalListProps) {
    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>
    );
  }

  function isModifierClick(event: React.MouseEvent) {
    const isMiddleClick = event.button === 1;
    return Boolean(
      isMiddleClick ||
        event.altKey ||
        event.ctrlKey ||
        event.metaKey ||
        event.shiftKey,
    );
  }
  ```
</CodeGroup>
