Nuxt Swiftsearch
Examples

Manual Widgets Mode

Keep legacy `:widgets` composition while using Swiftsearch components.

This pattern is used in the playground /search page to verify backwards compatibility.

pages/search.vue
<template>
  <AisInstantSearch :configuration="configuration" :widgets="widgets" instance-key="search">
    <AisSearchBox />
    <AisToggleRefinement attribute="free_shipping" />
    <AisRefinementList attribute="brand" searchable />
    <AisSortBy />
    <AisStats />
    <AisInfiniteHits />

    <AisIndex index="airbnb">
      <AisInfiniteHits />
    </AisIndex>
  </AisInstantSearch>
</template>

<script setup lang="ts">
import { algoliasearch } from "algoliasearch";
import { singleIndex as singleIndexMapping } from "instantsearch.js/es/lib/stateMappings";

const client = algoliasearch("latency", "6be0576ff61c053d5f9a3225e2a90f76");
const algoliaRouter = useAisRouter();

const airbnbIndex = useAisIndex({ indexName: "airbnb" });
airbnbIndex.addWidgets([useAisInfiniteHits({})]);

const widgets = computed(() => [
  useAisSortBy({
    items: [
      { value: "instant_search", label: "Default" },
      { value: "instant_search_price_asc", label: "Price asc." },
      { value: "instant_search_price_desc", label: "Price desc." },
    ],
  }),
  useAisStats({}),
  useAisInfiniteHits({ showPrevious: true }),
  useAisRefinementList({ attribute: "brand", showMore: true }, "brand-search"),
  useAisToggleRefinement({ attribute: "free_shipping" }),
  useAisSearchBox({}),
  airbnbIndex,
]);

const configuration = {
  indexName: "instant_search",
  searchClient: client,
  routing: {
    router: algoliaRouter.value.router,
    stateMapping: singleIndexMapping("instant_search"),
  },
};
</script>

Use this mode for dynamic widget generation, advanced branching, or when migrating incrementally.

Sharing a connector across components

When two components target the same connector and you want them to share state, register the composable with a widgetId and pass that same id to each component:

<template>
  <AisInstantSearch :configuration="configuration" :widgets="widgets">
    <!-- Both render against the same brand-search connector -->
    <AisRefinementList attribute="brand" id="brand-search" />
    <AisRefinementList attribute="brand" id="brand-search" />
  </AisInstantSearch>
</template>

<script setup lang="ts">
const widgets = computed(() => [
  useAisRefinementList({ attribute: "brand", showMore: true }, "brand-search"),
]);
</script>

Without a widgetId (default)

If you skip the second argument to useAis* and leave the component id unset, every instance still works — components fall back to a composable-level render-state map keyed by attribute. This means controls like toggleShowMore() and searchForItems() keep re-rendering the list inline (this is the path covered by the <AisRefinementList> regression tests):

<script setup lang="ts">
const widgets = computed(() => [
  useAisRefinementList({ attribute: "brand", showMore: true, limit: 3 }),
]);
</script>

<template>
  <AisInstantSearch :configuration="configuration" :widgets="widgets">
    <AisRefinementList attribute="brand" :show-more="true" :limit="3" />
  </AisInstantSearch>
</template>
If you need two independent connectors for the same attribute (for example two <AisRefinementList> blocks whose selections must not bleed into each other), give each component a distinct id AND register a matching useAis*({...}, "id-1") / useAis*({...}, "id-2") pair.
Copyright © 2026