API Reference / React InstantSearch Widgets / HierarchicalMenu
Apr. 24, 2019

HierarchicalMenu

Widget signature
<HierarchicalMenu
  attributes={string[]}
  // Optional parameters
  defaultRefinement={string}
  limit={number}
  showMore={boolean}
  showMoreLimit={number}
  separator={string}
  rootPath={string}
  showParentLevel={boolean}
  transformItems={function}
  translations={object}
/>

About this widget

The HierarchicalMenu widget is used to create a navigation based on a hierarchy of facet attributes. It is commonly used for categories with subcategories.

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"
  }
]

It’s also possible to 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 added in attributes for faceting, either on the dashboard or using attributesForFaceting with the API. By default, the separator we expect is > (with spaces) but you can use a different one by using the separator option.

Examples

1
2
3
4
5
6
7
8
9
10
import { HierarchicalMenu } from 'react-instantsearch-dom';

<HierarchicalMenu
  attributes={[
    'categories.lvl0',
    'categories.lvl1',
    'categories.lvl2',
    'categories.lvl3',
  ]}
/>

Props

attributes
type: string[]
Required

The names of the attributes inside the records.

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

The value of the item selected by default.

1
2
3
4
<HierarchicalMenu
  // ...
  defaultRefinement="Audio > Home Audio"
/>
limit
type: number
default: 10
Optional

The minimum number of facet values to retrieve.

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

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

1
2
3
4
<HierarchicalMenu
  // ...
  showMore
/>
showMoreLimit
type: number
default: 20
Optional

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

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

The level separator used in the records.

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

The path to use if the first level is not the root level.

1
2
3
4
<HierarchicalMenu
  // ...
  rootPath="Audio > Home Audio"
/>
showParentLevel
type: boolean
default: true
Optional

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

1
2
3
4
<HierarchicalMenu
  // ...
  showParentLevel={false}
/>
transformItems
type: function
Optional

Modifies the items being displayed, for example, to filter or sort them. It takes items as argument and expects them back in return.

1
2
3
4
5
6
7
8
9
<HierarchicalMenu
  // ...
  transformItems={items =>
    items.map(item => ({
      ...item,
      label: item.label.toUpperCase(),
    }))
  }
/>
translations
type: object
Optional

A mapping of keys to translation values.

  • showMore: the label of the “Show more” button. Accepts one boolean parameter that is true if the values are expanded, false otherwise.
1
2
3
4
5
6
7
8
<HierarchicalMenu
  // ...
  translations={{
    showMore(expanded) {
      return expanded ? 'Show less' : 'Show more';
    },
  }}
/>

Customize the UI - connectHierarchicalMenu

If you want to create your own UI of the HierarchicalMenu widget or use another UI library, you can use connectors.

Connectors are higher-order components. They encapsulate the logic for a specific kind of widget and they provide a way to interact with the InstantSearch context.

They have an outer component API that we call exposed props, and they provide some other props to the wrapped components which are called the provided props.

It’s a 3-step process:

// 1. Create a React component
const HierarchicalMenu = () => {
  // return the DOM output
};

// 2. Connect the component using the connector
const CustomHierarchicalMenu = connectHierarchicalMenu(HierarchicalMenu);

// 3. Use your connected widget
<CustomHierarchicalMenu />

Create a React component

const HierarchicalMenu = ({
  object[] items,
  string currentRefinement,
  function refine,
  function createURL,
}) => {
  // return the DOM output
};

Provided Props

items
type: object[]

The list of items the widget can display, with each item:

  • label: string: the label for the display of the refinement.
  • value: string: the value attached to the refinement.
  • count: number: the number of results that match the refinement.
  • isRefined: boolean: whether or not the value is selected.
  • items: object[]: th list of children, only when relevant (null otherwise).
1
2
3
4
5
6
7
8
9
10
11
12
const HierarchicalMenu = ({ items }) => (
  <ul>
    {items.map(item => (
      <li key={item.label}>
        <a href="#" style={{ fontWeight: item.isRefined ? 'bold' : '' }}>
          {item.label} ({item.count})
        </a>
        {item.items && <HierarchicalMenu items={item.items} />}
      </li>
    ))}
  </ul>
);
currentRefinement
type: string

The currently applied refinement.

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
const HierarchicalMenuList = ({ items, refine }) => (
  <ul>
    {items.map(item => (
      <li key={item.label}>
        <a
          href="#"
          style={{ fontWeight: item.isRefined ? 'bold' : '' }}
          onClick={event => {
            event.preventDefault();
            refine(item.value);
          }}
        >
          {item.label} ({item.count})
        </a>
        {item.items && (
          <HierarchicalMenuList items={item.items} refine={refine} />
        )}
      </li>
    ))}
  </ul>
);

const HierarchicalMenu = ({ items, currentRefinement, refine }) => (
  <div>
    <div>Current refinement: {currentRefinement}</div>
    <HierarchicalMenuList items={items} refine={refine} />
  </div>
);
refine
type: function

Toggles a refinement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const HierarchicalMenu = ({ items, refine }) => (
  <ul>
    {items.map(item => (
      <li key={item.label}>
        <a
          href="#"
          style={{ fontWeight: item.isRefined ? 'bold' : '' }}
          onClick={event => {
            event.preventDefault();
            refine(item.value);
          }}
        >
          {item.label} ({item.count})
        </a>
        {item.items && (
          <HierarchicalMenu
            items={item.items}
            refine={refine}
            />
          )}
      </li>
    ))}
  </ul>
);
createURL
type: function

Generates a URL for the corresponding search state.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const HierarchicalMenu = ({ items, createURL }) => (
  <ul>
    {items.map(item => (
      <li key={item.label}>
        <a
          href={createURL(item.value)}
          style={{ fontWeight: item.isRefined ? 'bold' : '' }}
        >
          {item.label} ({item.count})
        </a>
        {item.items && (
          <HierarchicalMenu
            items={item.items}
            createURL={createURL}
          />
        )}
      </li>
    ))}
  </ul>
);

Create and instantiate your connected widget

const CustomHierarchicalMenu = connectHierarchicalMenu(HierarchicalMenu);

<CustomHierarchicalMenu
  attributes={string[]}
  // Optional parameters
  defaultRefinement={string}
  limit={number}
  showMore={boolean}
  showMoreLimit={number}
  separator={string}
  rootPath={string}
  showParentLevel={boolean}
  transformItems={function}
/>

Exposed Props

attributes
type: string[]
Required

The names of the attributes inside the records.

1
2
3
4
5
6
7
8
<CustomHierarchicalMenu
  attributes={[
    'categories.lvl0',
    'categories.lvl1',
    'categories.lvl2',
    'categories.lvl3',
  ]}
/>
defaultRefinement
type: string
Optional

The value of the item selected by default.

1
2
3
4
<CustomHierarchicalMenu
  // ...
  defaultRefinement="Audio > Home Audio"
/>
limit
type: number
default: 10
Optional

The minimum number of diplayed items.

1
2
3
4
<CustomHierarchicalMenu
  // ...
  limit={20}
/>
showMore
type: boolean
default: false
Optional

We don’t expose the function to toggle the number of value displayed by the widget with the connector. Yet, the option is required to enable the “Show more” behavior. The option drives how many values are retrieved (either limit or showMoreLimit).

1
2
3
4
<CustomHierarchicalMenu
  // ...
  showMore
/>
showMoreLimit
type: number
default: 20
Optional

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

1
2
3
4
<CustomHierarchicalMenu
  // ...
  showMoreLimit={30}
/>
separator
type: string
default: >
Optional

The level separator used in the records.

1
2
3
4
<CustomHierarchicalMenu
  // ...
  separator="-"
/>
rootPath
type: string
Optional

The path to use if the first level is not the root level.

1
2
3
4
<CustomHierarchicalMenu
  // ...
  rootPath="Audio > Home Audio"
/>
showParentLevel
type: boolean
default: true
Optional

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

1
2
3
4
<CustomHierarchicalMenu
  // ...
  showParentLevel
/>
transformItems
type: function
Optional

Modifies the items being displayed, for example, to filter or sort them. It takes items as argument and expects them back in return.

1
2
3
4
5
6
7
8
9
10
11
12
13
<CustomHierarchicalMenu
  // ...
  transformItems={items => {
    const loop = innerItems =>
      innerItems.map(item => ({
        ...item,
        label: item.label.toUpperCase(),
        items: item.items ? loop(item.items) : item.items,
      }));

    return loop(items);
  }}
/>

If SEO is critical to your search page, your custom HTML markup needs to be parsable:

  • use plain <a> tags with href attributes for search engines bots to follow them,
  • use semantic markup with structured data when relevant, and test it.

Refer to our SEO checklist for building SEO-ready search experiences.

Full 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
import { connectHierarchicalMenu } from 'react-instantsearch-dom';

const HierarchicalMenu = ({ items, refine, createURL }) => (
  <ul>
    {items.map(item => (
      <li key={item.label}>
        <a
          href={createURL(item.value)}
          style={{ fontWeight: item.isRefined ? 'bold' : '' }}
          onClick={event => {
            event.preventDefault();
            refine(item.value);
          }}
        >
          {item.label} ({item.count})
        </a>
        {item.items && (
          <HierarchicalMenu
            items={item.items}
            refine={refine}
            createURL={createURL}
          />
        )}
      </li>
    ))}
  </ul>
);

const CustomHierarchicalMenu = connectHierarchicalMenu(HierarchicalMenu);

HTML output

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
<div class="ais-HierarchicalMenu">
  <ul class="ais-HierarchicalMenu-list ais-HierarchicalMenu-list--lvl0">
    <li class="ais-HierarchicalMenu-item ais-HierarchicalMenu-item--parent ais-HierarchicalMenu-item--selected">
      <a class="ais-HierarchicalMenu-link" href="#">
        <span class="ais-HierarchicalMenu-label">Appliances</span>
        <span class="ais-HierarchicalMenu-count">4,306</span>
      </a>
      <ul class="ais-HierarchicalMenu-list ais-HierarchicalMenu-list--child ais-HierarchicalMenu-list--lvl1">
        <li class="ais-HierarchicalMenu-item ais-HierarchicalMenu-item--parent">
          <a class="ais-HierarchicalMenu-link" href="#">
            <span class="ais-HierarchicalMenu-label">Dishwashers</span>
            <span class="ais-HierarchicalMenu-count">181</span>
          </a>
        </li>
        <li class="ais-HierarchicalMenu-item">
          <a class="ais-HierarchicalMenu-link" href="#">
            <span class="ais-HierarchicalMenu-label">Fans</span>
            <span class="ais-HierarchicalMenu-count">91</span>
          </a>
        </li>
      </ul>
    </li>
    <li class="ais-HierarchicalMenu-item ais-HierarchicalMenu-item--parent">
      <a class="ais-HierarchicalMenu-link" href="#">
        <span class="ais-HierarchicalMenu-label">Audio</span>
        <span class="ais-HierarchicalMenu-count">1,570</span>
      </a>
    </li>
  </ul>
  <button class="ais-HierarchicalMenu-showMore">Show more</button>
</div>

Did you find this page helpful?