UI libraries / Vue InstantSearch / Widgets
Signature
<ais-menu
  attribute="string"
  // Optional parameters
  :limit="number"
  :show-more="boolean"
  :show-more-limit="number"
  :sort-by="string[]|function"
  :transform-items="function"
  :class-names="object"
/>
Import
1
2
3
4
5
6
7
8
9
import { AisMenu } from 'vue-instantsearch';
// Use 'vue-instantsearch/vue3/es' for Vue 3

export default {
  components: {
    AisMenu
  },
  // ...
};

1. Follow additional steps in Optimize build size to ensure your code is correctly bundled.
2. This imports all the widgets, even the ones you don’t use. Read the Getting started guide for more information.

About this widget

The ais-menu widget displays a menu that lets the user choose a single value for a specific attribute.

As the ais-menu widget is internally based on a hierarchical refinement, you can not refine on a value including the default separator (>). Instead, you can use the ais-hierarchical-menu widget.

Requirements

The attribute passed to the attribute prop must be added in attributes for faceting, either on the dashboard or using attributesForFaceting with the API.

Examples

1
<ais-menu attribute="categories" />

Props

attribute
type: string
Required

The name of the attribute in the record.

1
<ais-menu attribute="categories" />
limit
type: number
default: 10
Optional

How many facet values to retrieve. When you enable the show-more feature, this is the number of facet values to display before clicking the “Show more” button.

1
2
3
4
<ais-menu
  [...]
  :limit="20"
/>
show-more
type: boolean
default: false
Optional

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

1
2
3
4
<ais-menu
  [...]
  show-more
/>
show-more-limit
type: number
default: 20
Optional

Amount of items to show if showing more. Requires show-more to be true.

1
2
3
4
<ais-menu
  [...]
  :show-more-limit="30"
/>
sort-by
type: string[]|function
default: Uses facetOrdering if set, ["name:asc", "count:desc"]

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"

It’s also possible to give a function, which receives items two by two, like JavaScript’s Array.sort.

If facetOrdering is set for this facet in renderingContent, and no value for sortBy is passed to this widget, facetOrdering is used, and the default order as a fallback.

When using an array, take steps to avoid creating infinite loops. When you use an array as a prop, it causes the widget to re-register on every render, and this can sometimes cause these infinite loops.

1
2
3
4
<ais-menu
  [...]
  :sort-by="['isRefined', 'count:desc']"
/>
transform-items
type: function
default: items => items
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
25
26
27
28
29
30
<template>
  <!-- ... -->
  <ais-menu
    [...]
    :transform-items="transformItems"
  />
</template>

<script>
  export default {
    methods: {
      transformItems(items) {
        return items.map(item => ({
          ...item,
          label: item.label.toUpperCase(),
        }));
      },

      /* or, combined with results */
      transformItems(items, { results }) {
        return items.map(item => ({
          ...item,
          label: item.isRefined
            ? `${item.label} (${results.nbPages} pages)`
            : item.label,
        }));
      },
    },
  };
</script>
class-names
type: object
default: {}
Optional

The CSS classes to override.

  • .ais-Menu: the root element of the widget.
  • .ais-Menu--noRefinement: the root element of the widget with no refinement.
  • .ais-Menu-list: the list of all menu items.
  • .ais-Menu-item: the menu list item.
  • .ais-Menu-item--selected: the selected menu list item.
  • .ais-Menu-link: the clickable menu element.
  • .ais-Menu-label: the label of each item.
  • .ais-Menu-count: the count of values for each item.
  • .ais-Menu-showMore: the button used to display more categories.
  • .ais-Menu-showMore--disabled: the disabled button used to display more categories.
1
2
3
4
5
6
7
8
<ais-menu
  [...]
  :class-names="{
    'ais-Menu': 'MyCustomMenu',
    'ais-Menu-link': 'MyCustomMenuLink',
    // ...
  }"
/>

Customize the UI

default

The slot to override the complete DOM output of the widget.

Note that when you implement this slot, none of the other slots will change the output, as the default slot surrounds them.

Scope

  • items: object[]: the values applicable to this menu.
  • canRefine: boolean: can the refinement be applied?
  • canToggleShowMore: boolean: is show-more possible?
  • isShowingMore: boolean: is show-more enabled?
  • refine: (value: string) => void: the function to select a refinement.
  • createURL: (value: string) => string: the function to return a link for this refinement.
  • sendEvent: (eventType: 'click', facetValue: 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.

Where each item is an object containing:

  • value: string: the value of the menu item.
  • label: string: the human-readable value of the menu item.
  • count: number: the number of results matched after a refinement is applied.
  • isRefined: boolean: indicates if the refinement is applied.
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
<ais-menu attribute="categories">
  <template
    v-slot="{
      items,
      canToggleShowMore,
      isShowingMore,
      toggleShowMore,
      refine,
      createURL,
      sendEvent,
    }"
  >
    <ul>
      <li v-for="item in items" :key="item.value">
        <a
          :href="createURL(item.value)"
          :style="{ fontWeight: item.isRefined ? 'bold': '' }"
          @click.prevent="refine(item.value)"
        >
          {{item.label}} - {{item.count}}
        </a>
      </li>
      <li>
        <button :disabled="!canToggleShowMore" @click="toggleShowMore">
          {{ isShowingMore ? 'Less' : 'More'}}
        </button>
      </li>
    </ul>
  </template>
</ais-menu>
showMoreLabel

The slot to override the DOM output for the label of the “Show more” button.

Scope

  • isShowingMore: boolean: is show-more enabled?
1
2
3
4
5
<ais-menu attribute="categories" show-more>
  <template v-slot:showMoreLabel="{ isShowingMore }">
    {{ isShowingMore ? 'Less' : 'More' }}
  </template>
</ais-menu>

HTML output

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="ais-Menu">
  <ul>
    <li>
      <a class="ais-Menu-link">
        <span class="ais-Menu-label">Apple</span>
        <span class="ais-Menu-count">50</span>
      </a>
    </li>
    <!-- more items -->
  </ul>
  <button class="ais-Menu-showMore">
    Show more
  </button>
</div>

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.

Did you find this page helpful?