npx agentspec-cli install 339ba802-21d2-4096-8409-38bcfcd9a7c6 --framework ClaudeCode→ .claude/skills/nuxt-swiftsearch/SKILL.md
## 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 `<script setup>`) AND a component (in `<template>`). The composable registers the widget with the InstantSearch instance; the component renders it.
```vue
<template>
<AisInstantSearch :widgets :configuration>
<AisSearchBox />
<AisHits />
<AisRefinementList attribute="brand" />
</AisInstantSearch>
</template>
<script setup lang="ts">
import { algoliasearch } from "algoliasearch";
const client = algoliasearch("APP_ID", "API_KEY");
const widgets = computed(() => [
useAisSearchBox({}),
useAisHits({ escapeHTML: true }),
useAisRefinementList({ attribute: "brand" }),
]);
const configuration = ref({
indexName: "my_index",
searchClient: client,
});
</script>
```
### 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 |
| -------------------------- | ----------------------------- | ------------------------------------------------------ |
| `<AisInstantSearch>` | — | Root wrapper, receives `:widgets` and `:configuration` |
| `<AisIndex>` | `useAisIndex()` | Multi-index scoping |
| `<AisSearchBox>` | `useAisSearchBox()` | Search input |
| `<AisHits>` | `useAisHits()` | Paginated results |
| `<AisInfiniteHits>` | `useAisInfiniteHits()` | Infinite scroll results |
| `<AisRefinementList>` | `useAisRefinementList()` | Facet filtering |
| `<AisMenu>` | `useAisMenu()` | Single-select facet menu |
| `<AisMenuSelect>` | — | Dropdown facet menu |
| `<AisHierarchicalMenu>` | `useAisHierarchicalMenu()` | Nested category navigation |
| `<AisNumericMenu>` | `useAisNumericMenu()` | Numeric range filtering |
| `<AisRangeInput>` | `useAisRangeInput()` | Min/max range input |
| `<AisRatingMenu>` | `useAisRatingMenu()` | Star rating filter |
| `<AisToggleRefinement>` | `useAisToggleRefinement()` | Boolean toggle filter |
| `<AisClearRefinements>` | `useAisClearRefinements()` | Clear all active filters |
| `<AisCurrentRefinements>` | `useAisCurrentRefinements()` | Show active filters |
| `<AisSortBy>` | `useAisSortBy()` | Sort order selector |
| `<AisStats>` | `useAisStats()` | Results count & timing |
| `<AisPagination>` | `useAisPagination()` | Page navigation |
| `<AisConfigure>` | `useAisConfigure()` | Hidden search parameters |
| `<AisHighlight>` | — | Highlight matched text |
| `<AisAutocomplete>` | `useAisAutocomplete()` | Autocomplete suggestions |
| `<AisPanel>` | — | Collapsible panel wrapper |
| `<AisQueryRuleCustomData>` | `useAisQueryRuleCustomData()` | Query rules data |
| `<AisStateResults>` | — | 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 <RouterConfig>{
parseQuery: qs.parse,
stringifyQuery: qs.stringify,
};
```
2. Add the router to configuration:
```vue
<script setup lang="ts">
const algoliaRouter = useAisRouter();
const configuration = ref({
indexName: "my_index",
routing: algoliaRouter.value,
searchClient: client,
});
</script>
```
### Slot Customization
All components expose slots with typed render state:
```vue
<AisHits>
<template #item="{ item }">
<div>{{ item.name }}</div>
</template>
</AisHits>
<AisInfiniteHits>
<template #item="{ item }">
<div>{{ item.title }}</div>
</template>
<template #loadMore="{ refineNext, isLastPage }">
<button :disabled="isLastPage" @click="refineNext">Load more</button>
</template>
</AisInfiniteHits>
```
### 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
<script setup lang="ts">
const { getInstance } = useAisInstantSearch();
// Access the raw InstantSearch instance for advanced use cases
</script>
```
### Multi-Index Search
```vue
<AisInstantSearch :widgets :configuration>
<AisIndex index="products">
<AisHits />
</AisIndex>
<AisIndex index="articles">
<AisHits />
</AisIndex>
</AisInstantSearch>
```
### 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)
Built on the Agent Skills standard
nuxt
Nuxt full-stack Vue framework with SSR, auto-imports, and file-based routing. Use when working with Nuxt apps, server routes, useFetch, middleware, or hybrid rendering.
by AgentHub Crawler
Aether
**Stack:** Nuxt 3 (SPA), Vue 3 Composition API (`<script setup>`), Vuetify 3, TypeScript (required), Auth0. **Structure:** `pages/` (file-based routing), `components/`, `composables/`, `server/api/`, `agents/` (Python ADK), `mcp-servers/` (Python FastMCP).
by AgentHub Crawler
Docker Ci Failing Configuration
Cursor IDE rules and conventions for AI-assisted development.
by AgentHub Crawler
Sign in to leave a comment.
Vue 3 Nuxt 3 Development Cursorrules Prompt File Rules
Cursor IDE rules and conventions for AI-assisted development.
by AgentHub Crawler