---
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
{{ item.name }}
{{ item.title }}
```
### 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)