Guides / Building Search UI

Upgrading Vue InstantSearch

Upgrade from v3 to v4

You can use Vue InstantSearch v4 with Vue 2 and Vue 3.

Server-side rendering

Using Vue InstantSearch v4 introduces one breaking change in the server-side rendering.

findResultsState() in serverPrefetch now takes component and renderToString: (app) => Promise<string> in the first argument.

Vue 3

1
2
3
4
5
6
7
8
9
10
import { renderToString } from '@vue/server-renderer';

// ...

serverPrefetch() {
  return this.instantsearch.findResultsState({
    component: this,
    renderToString,
  });
},

Vue 2

The renderToString function from vue-server-renderer/basic is callback-based. You need to promisify it like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import _renderToString from 'vue-server-renderer/basic';

function renderToString(app) {
  return new Promise((resolve, reject) => {
    _renderToString(app, (err, res) => {
      if (err) reject(err);
      resolve(res);
    });
  });
}

// ...

serverPrefetch() {
  return this.instantsearch.findResultsState({
    component: this,
    renderToString,
  });
},

If you upgrade to Vue 3

Importing the library

The path to import Vue InstantSearch has changed for Vue 3, and is now:

1
2
3
4
import InstantSearch from 'vue-instantsearch/vue3/es';

// ...
app.use(InstantSearch);

Vue Router v4

If you’re using Vue Router and upgrading it to v4, currentRoute is now a reference instead of an object.

1
2
3
4
5
// previously
vueRouter.currentRoute.query

// now
vueRouter.currentRoute.value.query

It’s not directly related to Vue InstantSearch, but if you are upgrading to Vue Router 4 along with Vue 3, you will encounter this issue anyway. It’s mentioned here for your information.

Upgrade from v2 to v3

This guide contains all the major changes that were introduced in v3, and how to migrate from v2.

Vue InstantSearch v3 uses InstantSearch.js v4, so all breaking changes using the InstantSearch.js API also apply here. You can find more information in its respective upgrade guide.

Federated search (multi-index)

If you were using a ais-configure or a ais-search-box to synchronize between two ais-instant-search instances, you can now replace either one of them with the new ais-index widget like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
  <ais-instant-search index-name="parent">
    <!-- root widgets are synchronized with every child -->
    <ais-searchbox />
    <ais-hits />
    
    <ais-index index-name="child">
      <!-- same query is used as parent -->
      <ais-hits />
      <!-- but this refinement list is only for the child -->
      <ais-refinement-list attribute="brands" />
    </ais-index>
  </ais-instant-search>
</template>

ais-autocomplete

The indices option has been removed in favor of ais-index.

This means that:

1
2
3
4
5
<template>
  <ais-autocomplete :indices="[{ name: 'additional' }]">
    <!-- custom rendering -->
  </ais-autocomplete>
</template>

Should be replaced with:

1
2
3
4
5
6
<template>
  <ais-index index-name="additional" />
  <ais-autocomplete>
    <!-- custom rendering -->
  </ais-autocomplete>
</template>

ais-state-results

This widget used to expose the search results only, you can now access the search parameters as well. This allows you to leverage query information to perform view logic instead of waiting for the response to retrieve the data.

1
2
3
4
5
<ais-state-results>
-  <template v-slot="{ query, nbHits }">
+  <template v-slot="{ state: { query }, results: { nbHits } }">
  </template>
<ais-state-results>

The results you have access to are always scoped to the current index.

Routing and URLs

Since the introduction of federated search, it’s now possible to have multiple indices in a single app. This means that the default state mapping for routing now takes multiple indices into account. If you want to keep on using the same URLs as before, make the following change:

1
2
3
4
5
6
7
8
9
10
11
12
13
- import { simple as simpleMapping } from 'instantsearch.js/es/lib/stateMappings';
+ import { singleIndex as singleIndexMapping } from 'instantsearch.js/es/lib/stateMappings';

export default {
  data() {
    return {
      routing: {
-        stateMapping: simpleMapping(),
+        stateMapping: singleIndexMapping('myIndex'),
      }
    }
  }
}

You can find more information on custom routers in the InstantSearch.js upgrade guide.

Server-side rendering (SSR)

This major version of Vue InstantSearch also includes an overhaul of the server-side rendering implementation. This takes two points into consideration:

  • work fully with serverPrefetch
  • no repetition needed between the template and parameters

To do this the following changes have been made:

  1. createInstantSearch -> createServerRootMixin
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { createServerRootMixin } from 'vue-instantsearch';

const app = new Vue({
  mixins: [
    createServerRootMixin({
      searchClient,
      indexName: 'instant_search',
    }),
  ],
  serverPrefetch() {
    return this.instantsearch.findResultsState(this);
  },
  beforeMount() {
    if (typeof window === 'object' && window.__ALGOLIA_STATE__) {
      this.instantsearch.hydrate(window.__ALGOLIA_STATE__);
      delete window.__ALGOLIA_STATE__;
    }
  },
  router,
  render: h => h(App),
});
  1. using a compatible router

The default routers in InstantSearch.js are based around browser location, and thus won’t work on the server. An example router for Vue SSR can be made like this:

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
import qs from 'qs';
// get this from the environment, will be different in nuxt vs. vue ssr
const serverUrl = context ? context.url : undefined;

const router = {
  read() {
    const url = serverUrl
      ? serverUrl
      : typeof window.location === 'object'
      ? window.location.href
      : '';
    const search = url.slice(url.indexOf('?'));

    return qs.parse(search, {
      ignoreQueryPrefix: true,
    });
  },
  write(routeState) {
    const query = qs.stringify(routeState, {
      addQueryPrefix: true,
    });

    if (typeof history === 'object') {
      history.pushState(routeState, null, query);
    }
  },
  createURL(routeState) {
    const query = qs.stringify(routeState, {
      addQueryPrefix: true,
    });

    return query;
  },
  onUpdate(callback) {
    if (typeof window !== 'object') {
      return;
    }

    this._onPopState = (event) => {
      if (this.writeTimer) {
        window.clearTimeout(this.writeTimer);
        this.writeTimer = undefined;
      }

      const routeState = event.state;

      // At initial load, the state is read from the URL without update.
      // Therefore the state object is not available.
      // In this case, we fallback and read the URL.
      if (!routeState) {
        callback(this.read());
      } else {
        callback(routeState);
      }
    };

    window.addEventListener('popstate', this._onPopState);
  },
  dispose() {
    if (this._onPopState && typeof window == 'object') {
      window.removeEventListener('popstate', this._onPopState);
    }

    // we purposely don't write on dispose, to prevent double entries on navigation
  },
};

A full example can be found in the GitHub repository. Read more detail in the dedicated SSR guide.

Helper v3.x.x

This release includes version 3 of the algoliasearch-helper package. If you are using the built-in widgets or connectors, nothing changes for you.

This version of algoliasearch-helper no longer includes Lodash, which significantly reduces its bundle size (from 27.5 KB to 9.1 KB Gzipped). If you’re using any methods from the helper, searchParameters or searchResults, refer to the detailed change log of the package.

Upgrade from v1 to v2

This guide contains all the major changes that were introduced in v2 and how to migrate from v1. You can still find the documentation for Vue InstantSearch v1.

Renamed components

Some components have been renamed to be be more consistent with other InstantSearch flavours.

  • ais-results -> ais-hits
  • ais-tree-menu -> ais-hierarchical-menu
  • ais-clear -> ais-clear-refinements
  • ais-results-per-page-selector -> ais-hits-per-page
  • ais-rating -> ais-rating-menu
  • ais-sort-by-selector -> ais-sort-by
  • ais-index -> ais-instant-search

All individual component exports have also been renamed from, for example, SearchBox to AisSearchBox. This is to make it more ergonomic to use them as components in a Vue file with the same name as expected.

The Component mixin has been renamed to createWidgetMixin({ connector }). Read more about that in the custom component guide.

New components

Find more information on these in their respective documentation.

1. ais-configure

This widget is the replacement of query-parameters on ais-index.

2. ais-state-results

This component can be used for conditional rendering, and getting information that’s not returned in ais-hits.

3. ais-breadcrumb

To be used together with ais-hierarchical-menu.

4. ais-menu-select

A single-selectable menu, rendered inside a select

5. ais-current-refinements

Shows the current refinements, and allows them to be unset.

6. ais-infinite-hits

Replaces :stack="true" on ais-results (now called ais-hits).

7. ais-numeric-menu

Statically set numerical ranges can be used to refine using this widget.

8. ais-panel

Wrap a widget in ais-panel to be able to give it a header and a footer. Replaces those options on each widget.

9. ais-toggle-refinement

Toggle a boolean refinement either refined/unrefined or refinement/another refinement. Useful for toggles or buttons with two states.

Renamed options

Some options have been renamed. Largely those are:

  • attribute-name -> attribute
  • results -> hits
  • anything in a list -> items / item
  • header / footer -> wrap the widget in an ais-panel

If you see anything not mentioned here, please complete the feedback form.

Removed options

  • query-parameters

This is now handled via the ais-configure widget. Each query parameter becomes a prop on Configure.

  • query

You can now synchronize the query across indices either by using a v-model on two ais-search-boxes of which you hide one, or with ais-configure on both indices, and synchronizing between those like that.

  • appId & apiKey

This is now handled by the search-client prop. Search client is what gets returned if you call algoliasearch.

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
  <template>
-   <ais-index
+   <ais-instant-search
-     app-id="appID"
-     api-key="apiKey"
+     :search-client="searchClient"
      index-name="myIndex"
    >
      <slot>My app</slot>
-   </ais-index>
+   </ais-instant-search>
  </template>

+ <script>
+ import algoliasearch from 'algoliasearch/lite';
+ const searchClient = algoliasearch('appID', 'apiKey');

+ export default {
+   data() {
+     return {
+       searchClient,
+     };
+   },
+ };
+ </script>
  • :stack="true"

When you used to put this on ais-results (now called ais-hits), it allows to load next pages without pagination, but with a “next page” button, as well as showing all pages rather than a single one. Replaced by ais-infinite-hits.

  • auto-search

This option is removed in favor of a more complete way of not doing queries using the search client. Information on that can be found in the conditional requests guide.

Removed components

  • ais-input

This component has been removed with as alternative having two options:

  1. use ais-search-box and style it to not include the extra items
  2. use the default slot of ais-search-box to use it with your own custom input (see live):
1
2
3
4
5
6
7
8
<ais-search-box autofocus>
  <input
    v-slot="{ currentRefinement, refine }"
    :value="currentRefinement"
    @input="refine($event.currentTarget.value)"
    placeholder="Custom SearchBox"
  />
</ais-search-box>

CSS class names

All CSS class names are now different, since Algolia follows the SUIT CSS methodology now, rather than the previous, slightly wrong, implementation of ‘block, element and modifier’ (BEM).

Since the DOM output is also different in most widgets, it’s best to start styling over from scratch on these widgets.

Each widget lists the CSS classes it uses in its documentation page.

Known limitations

SSR (server-side rendering)

The implementation of server-side rendering with Vue InstantSearch slightly changed, with a simpler API. Read more on how to use it in the SSR guide.

Search store

The search store no longer exists. Custom widgets are either made by making a connector, or a combination of new widgets.

You no longer need to copy a widget to give it custom rendering. Now you can fill in the default slot, which will have everything needed to render that component.

If you’re using this, and have suggestions or questions, please complete the feedback form.

Routing

You’re now able to use routing in InstantSearch with, or without Vue Router.

It’s possible to change props on ais-instant-search, except routing. If you have a need for that to be changed as well, please complete the feedback form.

Did you find this page helpful?