Vue Instantsearch


Using with Vue-Instantsearch components

By default, Algolia module only provides the search functionality but you can enable the vue-instantsearch components support to have Vue 3 components ready to serve as search and result components.

In order to enable them, first add instantSearch configuration option to module configuration:

import { defineNuxtConfig } from 'nuxt3'export default defineNuxtConfig({  modules: ['@nuxtjs/algolia'],  algolia: {    apiKey: process.env.ALGOLIA_SEARCH_API_KEY,    applicationId: process.env.ALGOLIA_APPLICATION_ID,    instantSearch: {      theme: 'algolia'    }  }})

You can choose a theme from satellite, reset, and algolia

Next, let's create indexName variable, call useAlgolia composable in page.vue script section to get the reference to Algolia, and import Vue Instantsearch components:

<script lang="ts" setup>const indexName = 'test_index' const algolia = useAlgoliaRef()import { AisInstantSearch, AisSearchBox, AisHits } from 'vue-instantsearch/vue3/es'</script>

Finally, let's use it in our page.vue template section with vue-instantsearch components:

<template>  <div>    <ais-instant-search :index-name="indexName" :search-client="algolia">      <ais-search-box />      <ais-hits />    </ais-instant-search>  </div></template>

Using vue-instantsearch with SSR

Server-side rendering requires a few extra steps and can be implemented by using two approaches.

With render function

To use the approach with render function, first we need to extract instantsearch instance from the mixin and provide it to all vue-instantsearch components:

import { createServerRootMixin } from 'vue-instantsearch/vue3/es'import { renderToString } from 'vue/server-renderer'const serverRootMixin = ref(  createServerRootMixin({    searchClient: algolia,    indexName,  }),)const { instantsearch } = serverRootMixin.value.data()provide('$_ais_ssrInstantSearchInstance', instantsearch)

Then load the results using useAsyncData and hydrate them on the client:

onBeforeMount(() => {  // Use data loaded on the server  if (algoliaState.value) {    instantsearch.hydrate(algoliaState.value)  }})const { data: algoliaState } = await useAsyncData('algolia-state', async () => {    if (import.meta.server) {        const nuxtApp = useNuxtApp();        nuxtApp.$algolia.transporter.requester = (            await import('@algolia/requester-node-http').then(                (lib) => lib.default || lib            )        ).createNodeHttpRequester();    }    return instantsearch.findResultsState({        // IMPORTANT: a component with access to `this.instantsearch` to be used by the createServerRootMixin code        component: {            $options: {                components: {                    AisInstantSearchSsr,                    AisIndex,                    AisConfigure,                    AisRefinementList,                    AisHits,                    AisHighlight,                    AisSearchBox,                    AisStats,                    AisPagination,                },                data() {                    return { instantsearch };                },                provide: { $_ais_ssrInstantSearchInstance: instantsearch },                render() {                    return h(AisInstantSearchSsr, null, () => [                        // Include any vue-instantsearch components that you use including each refinement attribute                        h(AisHits),                    ]);                },            },        },        renderToString,    });})

You can also check out the following Stackblitz link with the usage of above approach in SSR:

https://stackblitz.com/github/plexus77/nuxt-3-algolia-ssr?file=nuxt.config.ts

Without render function

As explained by Rigo here there is a way of having SSR Instantsearch without a need for using a render function:

<template>  <div>    <AisInstantSearchSsr>      <AisConfigure :hits-per-page.camel="4" v-if="$route.params.indexName === 'instant_search'"        :facet-filters.camel="`brand:${$route.params.brand}`" />      <AisRefinementList :attribute="$route.params.indexName === 'airbnb' ? 'room_type' : 'categories'        ">      </AisRefinementList>      <AisInfiniteHits show-previous>        <template #loadPrevious="{ isFirstPage, refinePrevious }">          <button :disabled="isFirstPage" @click="refinePrevious">            Load less          </button>        </template>        <template v-slot="{ items, refineNext, isLastPage }">          <div class="cont">            <div v-for="item in items" :key="item.objectID" class="item">              {{ item.name }}              <img :src="item.image ?? item.thumbnail_url" />              {{ item }}            </div>          </div>          <button :disabled="isLastPage" @click="refineNext">Load more</button>        </template>      </AisInfiniteHits>    </AisInstantSearchSsr>  </div></template><script>import { renderToString } from "vue/server-renderer";import {  AisInstantSearchSsr,  AisRefinementList,  AisInfiniteHits,  AisIndex,  AisConfigure,  // @ts-ignore} from "vue-instantsearch/vue3/es/index.js";export default defineNuxtComponent({  components: {    AisInstantSearchSsr,    AisRefinementList,    AisInfiniteHits,    AisIndex,    AisConfigure,  },  inject: ["$_ais_ssrInstantSearchInstance"],  async serverPrefetch() {    const s = await this["$_ais_ssrInstantSearchInstance"].findResultsState({      component: this,      renderToString,    });    this.$nuxt.ssrContext.payload.data.algoliaState = s;  },  props: {    indexName: {      type: String,      required: false,      default: null,    },  },  mounted() {    console.log(this.$_ais_ssrInstantSearchInstance);    setTimeout(() => { }, 5000);  },  async beforeMount() {    if (this.$nuxt.payload.data.algoliaState) {      this.$_ais_ssrInstantSearchInstance.hydrate(        this.$nuxt.payload.data.algoliaState,      );    } else {      // somehow, it needs to be disposed and refreshed when i change route with client side navigation      this.instantsearch.dispose();      this.$nextTick(() => {        this.$nextTick(() => {          this.instantsearch.helper.setIndex(this.indexName).search();        });      });    }    // avoid double hydration    delete this.$nuxt.payload.data.algoliaState;  },});</script>

Check out following link for more details

https://github.com/Rigo-m/nuxt-ssr-algolia-example/blob/main/components/InstantSearchProvider.vue