--- name: nuxt-swiftsearch description: Implement Algolia search UIs in Nuxt 3 apps using @atoms-studio/nuxt-swiftsearch — an SSR-first, fully typed alternative to vue-instantsearch. version: 1.0.0 license: MIT author: "@atoms-studio" tags: - nuxt - algolia - search - ssr - vue --- ## Instructions Use this skill when building search UIs in Nuxt 3 apps with Algolia via `@atoms-studio/nuxt-swiftsearch`. ### When to Use - Adding search functionality to a Nuxt 3 app with Algolia - Building search pages with filters, pagination, infinite scroll - Implementing SSR-compatible search experiences - Migrating from vue-instantsearch to a Nuxt-native approach ### Installation ```bash npx nuxi@latest module add swiftsearch ``` ```ts // nuxt.config.ts export default defineNuxtConfig({ modules: ["@atoms-studio/nuxt-swiftsearch"], }); ``` ### Core Pattern: Dual Registration Every widget requires **both** a composable (in ` ``` ### Critical Rules 1. **`widgets` must be `computed`** — not a plain array. The reactivity is required for SSR hydration. 2. **`configuration` must be `ref`** — wrap the config object in `ref()`. 3. **Attribute props must match** — when using `AisRefinementList`, `AisMenu`, etc., the `attribute` prop on the component must match the `attribute` in the composable. 4. **All composables and components are auto-imported** — no manual imports needed for `useAis*` composables or `Ais*` components. ### Available Components & Composables > Full props, slots, composable params, and examples for every widget: **[widgets-reference.md](./widgets-reference.md)** | Component | Composable | Purpose | | -------------------------- | ----------------------------- | ------------------------------------------------------ | | `` | — | Root wrapper, receives `:widgets` and `:configuration` | | `` | `useAisIndex()` | Multi-index scoping | | `` | `useAisSearchBox()` | Search input | | `` | `useAisHits()` | Paginated results | | `` | `useAisInfiniteHits()` | Infinite scroll results | | `` | `useAisRefinementList()` | Facet filtering | | `` | `useAisMenu()` | Single-select facet menu | | `` | — | Dropdown facet menu | | `` | `useAisHierarchicalMenu()` | Nested category navigation | | `` | `useAisNumericMenu()` | Numeric range filtering | | `` | `useAisRangeInput()` | Min/max range input | | `` | `useAisRatingMenu()` | Star rating filter | | `` | `useAisToggleRefinement()` | Boolean toggle filter | | `` | `useAisClearRefinements()` | Clear all active filters | | `` | `useAisCurrentRefinements()` | Show active filters | | `` | `useAisSortBy()` | Sort order selector | | `` | `useAisStats()` | Results count & timing | | `` | `useAisPagination()` | Page navigation | | `` | `useAisConfigure()` | Hidden search parameters | | `` | — | Highlight matched text | | `` | `useAisAutocomplete()` | Autocomplete suggestions | | `` | — | Collapsible panel wrapper | | `` | `useAisQueryRuleCustomData()` | Query rules data | | `` | — | Generic state renderer | ### Widget Categories (Quick Lookup) **Search Input:** `AisSearchBox`, `AisAutocomplete` **Results:** `AisHits`, `AisInfiniteHits`, `AisStateResults` **Refinements:** `AisRefinementList`, `AisMenu`, `AisMenuSelect`, `AisHierarchicalMenu`, `AisNumericMenu`, `AisRangeInput`, `AisRatingMenu`, `AisToggleRefinement` **Active Filters:** `AisClearRefinements`, `AisCurrentRefinements` **Sorting & Pagination:** `AisSortBy`, `AisPagination` **Display:** `AisStats`, `AisHighlight`, `AisPanel` **Config:** `AisConfigure`, `AisQueryRuleCustomData` **Structure:** `AisInstantSearch`, `AisIndex` ### URL Routing To sync search state with the URL: 1. Set up query string parsing: ```ts // app/router.options.ts import type { RouterConfig } from "@nuxt/schema"; import qs from "qs"; export default { parseQuery: qs.parse, stringifyQuery: qs.stringify, }; ``` 2. Add the router to configuration: ```vue ``` ### Slot Customization All components expose slots with typed render state: ```vue ``` ### Caching (Infinite Hits) For stateful caching across page navigations: ```ts const cache = useAisStatefulCache(); const infiniteCache = useAisInfiniteHitsStatefulCache(); const widgets = computed(() => [useAisInfiniteHits({ cache: infiniteCache })]); ``` ### Accessing the Instance ```vue ``` ### Multi-Index Search ```vue ``` ### CSS Classes Components use BEM convention: `ais-WidgetName-element--modifier`. Use `useSuit("WidgetName")` to generate class names programmatically. ### Common Mistakes - Forgetting to add the composable to `widgets` array (component renders but doesn't work) - Using a plain array instead of `computed()` for widgets - Mismatched `attribute` between component prop and composable param - Importing composables manually (they're auto-imported) ## Dependencies - Nuxt >= 3.10 - `algoliasearch` v5+ - `qs` (only if using routing)