UI libraries / InstantSearch.js / Widgets
Signature
breadcrumb({
  container: string|HTMLElement,
  attributes: string[],
  // Optional parameters
  rootPath: string,
  separator: string,
  templates: object,
  cssClasses: object,
  transformItems: function,
});
Import
1
import { breadcrumb } from 'instantsearch.js/es/widgets';

About this widget

The breadcrumb widget is a secondary navigation scheme that lets users see where the current page is in relation to the facet’s hierarchy.

It reduces the number of actions a user needs to take to get to a higher-level page and improves the discoverability of the app or website’s sections and pages. It is commonly used for websites with lot of data, organized into categories with subcategories.

Requirements

The objects to use in the breadcrumb 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 in attributes for faceting, either on the dashboard or using the attributesForFaceting parameter with the API.

By default, the separator is > (with spaces), but you can use a different one by using the separator option.

If there is also a hierarchicalMenu on the page, it must follow the same configuration.

Examples

1
2
3
4
5
6
7
8
breadcrumb({
  container: '#breadcrumb',
  attributes: [
    'hierarchicalCategories.lvl0',
    'hierarchicalCategories.lvl1',
    'hierarchicalCategories.lvl2',
  ],
});

Options

container
type: string|HTMLElement
Required

The CSS Selector or HTMLElement to insert the widget into.

1
2
3
4
breadcrumb({
  // ...
  container: '#breadcrumb',
});
attributes
type: string[]
Required

An array of attributes to generate the breadcrumb.

1
2
3
4
5
6
7
8
breadcrumb({
  // ...
  attributes: [
    'hierarchicalCategories.lvl0',
    'hierarchicalCategories.lvl1',
    'hierarchicalCategories.lvl2',
  ],
});
rootPath
type: string
Optional

The path to use if the first level is not 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
16
instantsearch({
  // ...
  initialUiState: {
    YourIndexName: {
      // breadcrumbs share their UI state with hierarchical menus
      hierarchicalMenu: {
        'hierarchicalCategories.lvl0': ['Audio'],
      },
    },
  },
}).addWidgets([
  breadcrumb({
    // ...
    rootPath: 'Audio',
  }),
]);
separator
type: string
default: >
Optional

The level separator used in the records.

1
2
3
4
breadcrumb({
  // ...
  separator: ' / ',
});
templates
type: object
Optional

The templates to use for the widget.

1
2
3
4
5
6
breadcrumb({
  // ...
  templates: {
    // ...
  },
});
cssClasses
type: object
Optional

The CSS classes you can override:

  • root: the root element of the widget.
  • noRefinementRoot: the root element if there are no refinements.
  • list: the list of results.
  • item: the list items. They contain the link and separator.
  • selectedItem: the selected item in the list. This is the last one, or the root one if there are no refinements.
  • separator: the separator.
  • link: the links in each item.
1
2
3
4
5
6
7
8
9
10
breadcrumb({
  // ...
  cssClasses: {
    root: 'MyCustomBreadcrumb',
    list: [
      'MyCustomBreadcrumbList',
      'MyCustomBreadcrumbList--sub-class',
    ],
  },
});
transformItems
type: function
Optional

Receives the items and is called before displaying them. Should return a new array with the same shape as the original array. Useful for 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
16
17
18
19
20
21
22
23
24
breadcrumb({
  // ...
  transformItems(items) {
    return items.map(item => ({
      ...item,
      label: item.label.toUpperCase(),
    }));
  },
});

/* or, combined with results */
breadcrumb({
  // ...
  transformItems(items, { results }) {
    const lastItem = items.pop();
    return [
      ...items,
      {
        ...lastItem,
        label: `${lastItem.label} (${results.nbHits} hits)`,
      },
    ];
  },
});

Templates

You can customize parts of the widget’s UI using the Templates API.

Every template provides an html function you can use as a tagged template. Using html lets you safely provide templates as an HTML string. It works directly in the browser without a build step. See Templating your UI for more information.

The html function is available starting from v4.46.0.

home
type: string|function
Optional

The label of the breadcrumb’s first element.

1
2
3
4
5
6
7
8
breadcrumb({
  // ...
  templates: {
    home(data, { html }) {
      return html`<span>Home</span>`;
    },
  },
});
separator
type: string|function
Optional

The symbol used to separate the elements of the breadcrumb.

1
2
3
4
5
6
7
8
breadcrumb({
  // ...
  templates: {
    separator(data, { html }) {
      return html`<span> > </span>`;
    },
  },
});

HTML output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div class="ais-Breadcrumb">
  <ul class="ais-Breadcrumb-list">
    <li class="ais-Breadcrumb-item">
      <a class="ais-Breadcrumb-link" href="#">Home</a>
    </li>
    <li class="ais-Breadcrumb-item">
      <span class="ais-Breadcrumb-separator"> &gt; </span>
      <a class="ais-Breadcrumb-link" href="#">Cameras &amp; Camcorders</a>
    </li>
    <li class="ais-Breadcrumb-item ais-Breadcrumb-item--selected">
      <span class="ais-Breadcrumb-separator"> &gt; </span>
      Digital Cameras
    </li>
  </ul>
</div>

Customize the UI with connectBreadcrumb

If you want to create your own UI of the breadcrumb widget, you can use connectors.

To use connectBreadcrumb, you can import it with the declaration relevant to how you installed InstantSearch.js.

1
import { connectBreadcrumb } from 'instantsearch.js/es/connectors';

Then it’s a 3-step process:

// 1. Create a render function
const renderBreadcrumb = (renderOptions, isFirstRender) => {
  // Rendering logic
};

// 2. Create the custom widget
const customBreadcrumb = connectBreadcrumb(
  renderBreadcrumb
);

// 3. Instantiate
search.addWidgets([
  customBreadcrumb({
    // Widget parameters
  })
]);

Create a render function

This rendering function is called before the first search (init lifecycle step) and each time results come back from Algolia (render lifecycle step).

const renderBreadcrumb = (renderOptions, isFirstRender) => {
  const {
    object[] items,
    boolean canRefine,
    function refine,
    function createURL,
    object widgetParams,
  } = renderOptions;

  if (isFirstRender) {
    // Do some initial rendering and bind events
  }

  // Render the widget
};

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.

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

Rendering options

items
type: object[]
Required

The items to render, containing the keys:

  • label: the label of the category or subcategory.
  • value: the value of breadcrumb item.
1
2
3
4
5
6
7
8
9
const renderBreadcrumb = (renderOptions, isFirstRender) => {
  const { items } = renderOptions;

  document.querySelector('#breadcrumb').innerHTML = `
    <ul>
      ${items.map(item => `<li>${item.label}</li>`).join('')}
    </ul>
  `;
};
canRefine
type: boolean
Required

Indicates if search state can be refined.

1
2
3
4
5
6
7
8
9
const renderBreadcrumb = (renderOptions, isFirstRender) => {
  const { items, canRefine, refine } = renderOptions;

  const container = document.querySelector('#breadcrumb');
  if (!canRefine) {
    container.innerHTML = '';
    return;
  }
};
refine
type: function
default: (item.value) => undefined
Required

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

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
const renderBreadcrumbItem = item => `
  <li>
    ${
      item.value
        ? `<a href="#" data-value="${item.value}">${item.label}</a>`
        : `<span>${item.label}</span>`
    }
  </li>`;

const renderBreadcrumb = (renderOptions, isFirstRender) => {
  const { items, refine } = renderOptions;

  const container = document.querySelector('#breadcrumb');

  container.innerHTML = `
    <ul>
      <li>
        <a href="#" data-value="">Home</a>
      </li>
      ${items.map(renderBreadcrumbItem).join('')}
    </ul>
  `;

  [...container.querySelectorAll('a')].forEach(
    element => {
      element.addEventListener('click', event => {
        event.preventDefault();
        refine(event.currentTarget.dataset.value);
      });
    }
  );
};
createURL
type: function
default: (item.value) => string
Required

Generates a URL of the next state of a clicked item. The special value null is used for the root item of the breadcrumb and returns an empty URL.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const renderBreadcrumb = (renderOptions, isFirstRender) => {
  const { items, createURL } = renderOptions;

  document.querySelector('#breadcrumb').innerHTML = `
    <ul>
      ${items
        .map(
          item =>
            `<li>
              <a href="${createURL(item.value)}">${item.label}</a>
            </li>`
        )
        .join('')}
    </ul>
  `;
};
widgetParams
type: object

All original widget options forwarded to the render function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const renderBreadcrumb = (renderOptions, isFirstRender) => {
  const { widgetParams } = renderOptions;

  widgetParams.container.innerHTML = '...';
};

// ...

search.addWidgets([
  customBreadcrumb({
    // ...
    container: document.querySelector('#breadcrumb'),
  })
]);

Create and instantiate the custom widget

We first create custom widgets from our rendering function, then we instantiate them. When doing that, there are two types of parameters you can give:

  • Instance parameters: they are predefined parameters that you can use to configure the behavior of Algolia.
  • Your own parameters: to make the custom widget generic.

Both instance and custom parameters are available in connector.widgetParams, inside the renderFunction.

const customBreadcrumb = connectBreadcrumb(
  renderBreadcrumb
);

search.addWidgets([
  customBreadcrumb({
    attributes: string[],
    // Optional instance params
    rootPath: string,
    separator: string,
    transformItems: function,
  })
]);

Instance options

attributes
type: string[]
Required

The attributes to use to generate the hierarchy of the breadcrumb.

1
2
3
4
5
6
7
customBreadcrumb({
  attributes: [
    'hierarchicalCategories.lvl0',
    'hierarchicalCategories.lvl1',
    'hierarchicalCategories.lvl2',
  ],
});
rootPath
type: string
Optional

The path to use if the first level is not 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
customBreadcrumb({
  // ...
  rootPath: 'Audio',
});
separator
type: string
default: >
Optional

The level separator used in the records.

1
2
3
4
customBreadcrumb({
  // ...
  separator: ' / ',
});
transformItems
type: function
Optional

Receives the items and is called before displaying them. Should return a new array with the same shape as the original array. Useful for 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
16
17
18
19
20
21
22
23
24
customBreadcrumb({
  // ...
  transformItems(items) {
    return items.map(item => ({
      ...item,
      label: item.label.toUpperCase(),
    }));
  },
});

/* or, combined with results */
customBreadcrumb({
  // ...
  transformItems(items, { results }) {
    const lastItem = items.pop();
    return [
      ...items,
      {
        ...lastItem,
        label: `${lastItem.label} (${results.nbHits} hits)`,
      },
    ];
  },
});

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
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
// Create the render function
const renderBreadcrumbItem = ({ item, createURL }) => `
  <li>
    ${
      item.value
        ? `<a
            href="${createURL(item.value)}"
            data-value="${item.value}"
          >
            ${item.label}
          </a>`
        : `<span>${item.label}</span>`
    }
  </li>`;

const renderBreadcrumb = (renderOptions, isFirstRender) => {
  const { items, refine, createURL, widgetParams } = renderOptions;

  widgetParams.container.innerHTML = `
    <ul>
      <li>
        <a href="#" data-value="">Home</a>
      </li>
      ${items
        .map(item =>
          renderBreadcrumbItem({
            item,
            createURL,
          })
        )
        .join('')}
    </ul>
  `;

  [...widgetParams.container.querySelectorAll('a')].forEach(element => {
    element.addEventListener('click', event => {
      event.preventDefault();
      refine(event.currentTarget.dataset.value);
    });
  });
};

// Create the custom widget
const customBreadcrumb = connectBreadcrumb(
  renderBreadcrumb
);

// Instantiate the custom widget
search.addWidgets([
  customBreadcrumb({
    container: document.querySelector('#breadcrumb'),
    attributes: [
      'hierarchicalCategories.lvl0',
      'hierarchicalCategories.lvl1',
      'hierarchicalCategories.lvl2',
    ],
  })
]);
Did you find this page helpful?