🎉 Try the public beta of the new docs site at algolia.com/doc-beta! 🎉
Guides / Building Search UI / UI & UX patterns / Query Suggestions

Build a Query Suggestions UI with Vue InstantSearch

To help users with their search, Algolia provides Query Suggestions. This feature creates an index of your user’s best queries. You can then use this index to propose suggestions to your users as they’re typing into the ais-search-box. Once you’ve configured the generation of the Query Suggestions index, you need to query this index as well. Use multi-index search to do that.

This guide shows how to use a search box to display a list of suggestions and their associated categories. Once the user selects a suggestion, the engine will apply the query and the category.

If you’re building an autocomplete with Query Suggestions, you should use the Autocomplete library which lets you build a full-featured, accessible autocomplete experience. This is the recommended way of building an autocomplete search with Algolia.

Working example

This code has been specifically created for Vue 2. Some modifications may be required for it to work correctly in Vue 3.

Refine your results with the suggestions

The first step is to set up a custom autocomplete component with the Vue Autosuggest library. You must then wrap the component with the ais-autocomplete connector. You can find more information in the guide on autocomplete.

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
<template>
  <ais-instant-search
    :search-client="searchClient"
    index-name="instant_search_demo_query_suggestions"
  >
    <ais-configure :hitsPerPage="5" />
    <ais-autocomplete>
      <template v-slot="{ currentRefinement, indices, refine }">
        <vue-autosuggest
          :suggestions="indicesToSuggestions(indices)"
          @selected="onSelect"
          :input-props="{
            style: 'width: 100%',
            onInputChange: refine,
            placeholder: 'Search here…',
          }"
        >
          <template v-slot="{ suggestion }">
            <ais-highlight attribute="query" :hit="suggestion.item" />
          </template>
        </vue-autosuggest>
      </template>
    </ais-autocomplete>
  </ais-instant-search>
</template>

<script>
import { VueAutosuggest } from 'vue-autosuggest';

export default {
  components: { VueAutosuggest },
  methods: {
    onSelect(selected) {
      if (selected) {
        console.log('selected');
      }
    },
    indicesToSuggestions(indices) {
      return indices.map(({ hits }) => ({
        data: hits.map(hit => {
          return {
            ...hit,
            // this is for Vue AutoSuggest
            name: hit.query,
          };
        }),
      }));
    },
  },
};
</script>

Now that you have your autocomplete component, you can create the multi-index search experience. The ais-autocomplete widget reads from two indices: instant_search_demo_query_suggestions and instant_search. The first index widget is implied by the creation of the ais-instant-search widget. You can find more information in the autocomplete guide.

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
66
67
68
69
70
71
72
<template>
  <div>
    <ais-instant-search
      :search-client="searchClient"
      index-name="instant_search_demo_query_suggestions"
    >
      <ais-configure :hitsPerPage="5" />
      <ais-index index-name="instant_search" />
      <ais-autocomplete>
        <template v-slot="{ currentRefinement, indices, refine }">
          <vue-autosuggest
            :suggestions="indicesToSuggestions(indices)"
            @selected="onSelect"
            :input-props="{
              style: 'width: 100%',
              onInputChange: refine,
              placeholder: 'Search here…',
            }"
          >
          <template v-slot="{ suggestion }">
            <ais-highlight
              :hit="suggestion.item"
              attribute="query"
              v-if="suggestion.item.query"
            />
            <ais-highlight
              :hit="suggestion.item"
              attribute="name"
              v-else
            />
            <strong v-if="suggestion.item.price">
              $ {{ suggestion.item.price }}
            </strong>
            <img
              :src="suggestion.item.image"
              style="height: 50px;"
              v-if="suggestion.item.image"
            />
          </template>
          </vue-autosuggest>
        </template>
      </ais-autocomplete>
    </ais-instant-search>
  </div>
</template>

<script>
import { VueAutosuggest } from 'vue-autosuggest';

export default {
  components: { VueAutosuggest },
  methods: {
    onSelect(selected) {
      if (selected) {
        const { query } = selected.item;
        this.query = query;
      }
    },
    indicesToSuggestions(indices) {
      return indices.map(({ hits }) => ({
        data: hits.map(hit => {
          return {
            ...hit,
            // this is for Vue Autosuggest
            name: hit.query,
          };
        }),
      }));
    },
  },
};
</script>

That’s it. You have set up your autocomplete multi-index search experience, and users can now select a suggestion and use it to search the main index.

A typical use of autocomplete is to display both relevant categories and suggestions. Then when a user selects a suggestion, both the suggestion and the associated category are used to refine the search. For this example, the relevant categories are stored on the suggestions records. You must update your render function to display the categories with the suggestions. For simplicity and brevity of the code, assume that all suggestions have categories, but this isn’t the case in the actual dataset. Take a look at the complete example to see the actual implementation.

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
<template>
  <!-- ... -->
    <template v-slot="{ suggestion }">
      <span>
        <ais-highlight attribute="query" :hit="suggestion.item" />
        <span>
          <em>
            in
            <span v-if="suggestion.item.category">
              {{ suggestion.item.category.value }}
            </span>
            <span v-else> All categories </span>
          </em>
        </span>
      </span>
    </template>
  <!-- ... -->
</template>

<script>
export default {
  methods:
    indicesToSuggestions(indices) {
      return indices.map(({ hits }) => ({
        data: hits.map(hit => {
          // categories is an array, but you only want the first element
          // hit.instant_search is undefined if there are no matched categories
          const [category] =
            (hit.instant_search &&
              hit.instant_search.facets.exact_matches.categories) ||
            [];

          return {
            ...hit,
            category,
            name: hit.query,
          };
        }),
      }));
    },
  },
};
</script>

Now that you can display categories, you can use them to refine the main search. Use the ais-configure widget with disjunctiveFacets and disjunctiveFacetsRefinement. These two parameters are the same as internally used in a refinement list.

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
<template>
  <!-- ... -->
  <ais-instant-search
    :search-client="searchClient"
    index-name="instant_search"
  >
    <!-- optionally to display the selected category -->
    <ais-current-refinements />
    <ais-configure
      :query="query"
      :disjunctiveFacets="['categories']"
      :disjunctiveFacetsRefinements="disjunctiveFacetsRefinements"
    />
    <!-- ... -->
  </ais-instant-search>
</template>

<script>
export default {
  data() {
    return {
      query: '',
      disjunctiveFacetsRefinements: undefined,
    };
  },
  methods: {
    onSelect(selected) {
      if (selected) {
        const { query, category } = selected.item;
        this.query = query;
        this.disjunctiveFacetsRefinements = category && {
          categories: [category.value],
        };
      }
    },
  },
};
</script>

That’s it. When a suggestion is selected, both the query and the category are applied to the main search.

Did you find this page helpful?