Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 21 additions & 18 deletions skills/instantsearch/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ description: >-
license: MIT
metadata:
author: algolia
version: '1.0'
version: '1.1'
---

# InstantSearch
Expand All @@ -29,7 +29,10 @@ InstantSearch comes in three libraries. Detect which one applies from the projec

Once identified, use the matching reference directory (e.g., `references/react/`) for all technology rules, anti-patterns, styling, glossary, and pattern-specific guidance.

> **Note:** This skill currently has references for **React InstantSearch** only. If the project uses Vue or vanilla JS, let the user know and ask how they'd like to proceed.
> **Note:** Coverage by library:
>
> - **React InstantSearch**: full coverage (autocomplete, search results page, custom widgets, middleware, SSR).
> - **Vue InstantSearch** and **InstantSearch.js**: library-level rules and autocomplete only. For other patterns, follow the [Source-of-truth check](#2-source-of-truth-check) and ask the user before scaffolding non-trivial flows.

## Workflow

Expand Down Expand Up @@ -68,8 +71,8 @@ Follow the library's source-of-truth reference for the exact commands and URL pa
| Library | Reference |
| ------- | ---------------------------------------------------------- |
| React | [source-of-truth.md](references/react/source-of-truth.md) |
| Vue | |
| JS | |
| Vue | [source-of-truth.md](references/vue/source-of-truth.md) |
| JS | [source-of-truth.md](references/js/source-of-truth.md) |

The reference always covers, in order:

Expand All @@ -86,23 +89,23 @@ Pick the matching pattern reference for the library and the user's request. If n

Patterns available for each library:

| Pattern | React | Vue | JS |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --- | --- |
| Autocomplete | [features](references/react/autocomplete/features.md), [styling](references/react/autocomplete/styling.md), [anti-patterns](references/react/autocomplete/anti-patterns.md) | | |
| Search results page (incl. faceted search, sort, pagination) | [features](references/react/search-results-page/features.md), [styling](references/react/search-results-page/styling.md), [anti-patterns](references/react/search-results-page/anti-patterns.md) | — | — |
| Pattern | React | Vue | JS |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Autocomplete | [features](references/react/autocomplete/features.md), [styling](references/react/autocomplete/styling.md), [anti-patterns](references/react/autocomplete/anti-patterns.md) | [features](references/vue/autocomplete/features.md), [styling](references/vue/autocomplete/styling.md), [anti-patterns](references/vue/autocomplete/anti-patterns.md) | [features](references/js/autocomplete/features.md), [styling](references/js/autocomplete/styling.md), [anti-patterns](references/js/autocomplete/anti-patterns.md) |
| Search results page (incl. faceted search, sort, pagination) | [features](references/react/search-results-page/features.md), [styling](references/react/search-results-page/styling.md), [anti-patterns](references/react/search-results-page/anti-patterns.md) | — | — |

Also read and apply the library-level references (apply regardless of pattern):

| Reference | React | Vue | JS |
| ---------------- | ----------------------------------------------------------- | --- | --- |
| Technology rules | [technology-rules.md](references/react/technology-rules.md) | — | — |
| Anti-patterns | [anti-patterns.md](references/react/anti-patterns.md) | | — |
| Styling | [styling.md](references/react/styling.md) | | — |
| Glossary | [glossary.md](references/react/glossary.md) | | — |
| Source of truth | [source-of-truth.md](references/react/source-of-truth.md) | | |
| Custom widgets | [custom-widgets.md](references/react/custom-widgets.md) | — | — |
| Middleware | [middleware.md](references/react/middleware.md) | — | — |
| SSR | [ssr.md](references/react/ssr.md) | — | — |
| Reference | React | Vue | JS |
| ---------------- | ----------------------------------------------------------- | --------------------------------------------------------- | -------------------------------------------------------- |
| Technology rules | [technology-rules.md](references/react/technology-rules.md) | [technology-rules.md](references/vue/technology-rules.md) | [technology-rules.md](references/js/technology-rules.md) |
| Anti-patterns | [anti-patterns.md](references/react/anti-patterns.md) | [anti-patterns.md](references/vue/anti-patterns.md) | [anti-patterns.md](references/js/anti-patterns.md) |
| Styling | [styling.md](references/react/styling.md) | [styling.md](references/vue/styling.md) | [styling.md](references/js/styling.md) |
| Glossary | [glossary.md](references/react/glossary.md) | [glossary.md](references/vue/glossary.md) | [glossary.md](references/js/glossary.md) |
| Source of truth | [source-of-truth.md](references/react/source-of-truth.md) | [source-of-truth.md](references/vue/source-of-truth.md) | [source-of-truth.md](references/js/source-of-truth.md) |
| Custom widgets | [custom-widgets.md](references/react/custom-widgets.md) | — | — |
| Middleware | [middleware.md](references/react/middleware.md) | — | — |
| SSR | [ssr.md](references/react/ssr.md) | — | — |

Consult types and live docs first (Step 2). Ask the user only if both fail. Never fall back to legacy libraries or guessed APIs.

Expand Down
30 changes: 30 additions & 0 deletions skills/instantsearch/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,36 @@
"Does NOT enable a flag that does not appear in the installed type",
"Asks the user before enabling any flag whose default cannot be determined from types or docs"
]
},
{
"id": 11,
"prompt": "Add an autocomplete search bar to my Vue 3 app. I'm using vue-instantsearch elsewhere on the site for a results page. The autocomplete should query my products index and clicking a result should navigate to /products/[objectID].",
"expected_output": "Autocomplete built with @algolia/autocomplete-js mounted from a Vue component (onMounted/onBeforeUnmount), reusing the module-level searchClient",
"files": [],
"expectations": [
"Picks @algolia/autocomplete-js as the autocomplete library, NOT a vue-instantsearch widget such as <ais-search-box> or <ais-autocomplete>",
"Does NOT treat @algolia/autocomplete-js as an anti-pattern (it is the recommended path for Vue, unlike React)",
"Mounts autocomplete inside a Vue component via onMounted with a ref to the host element, and calls instance.destroy() in onBeforeUnmount",
"Declares the searchClient at module scope (outside the component), reusing the same one used by vue-instantsearch where applicable",
"Configures getSources with an Algolia source that uses algoliasearch v5's search method shape",
"Wires getItemUrl and a templates.item that links to /products/[objectID]",
"Confirms the autocomplete options shape (getSources, templates) against the live Algolia docs or installed types before scaffolding"
]
},
{
"id": 12,
"prompt": "Add autocomplete search to a vanilla JS site. The page already mounts an InstantSearch.js results page. The autocomplete should sit in the header and link to product detail pages.",
"expected_output": "Autocomplete built with @algolia/autocomplete-js, separate from the instantsearch.js widget tree, sharing the same module-level searchClient",
"files": [],
"expectations": [
"Picks @algolia/autocomplete-js as the autocomplete library, NOT the instantsearch.js searchBox widget for autocomplete behavior",
"Does NOT treat @algolia/autocomplete-js as an anti-pattern (it is the recommended path for vanilla JS, unlike React)",
"Calls autocomplete({ container, getSources, ... }) once and saves the returned instance for later destroy() on teardown",
"Reuses the module-level searchClient already used by instantsearch.js, declared outside any function",
"Uses algoliasearch v5's search method shape inside getSources",
"Imports @algolia/autocomplete-theme-classic (or documents the chosen theme) so the dropdown is styled",
"Confirms the autocomplete options shape against the live Algolia docs or installed types before scaffolding"
]
}
]
}
16 changes: 16 additions & 0 deletions skills/instantsearch/references/js/anti-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Anti-patterns (InstantSearch.js)

These supplement the [technology rules](technology-rules.md). For anything not listed here, run the [Source-of-truth check](source-of-truth.md).

| Anti-pattern | Why it's wrong | What to do instead |
| ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| Calling `instantsearch({...})` more than once on the same page | Multiple instances duplicate searches and conflict over routing | Create one instance in module scope; share it across mount points |
| Calling `search.start()` more than once | Re-initializes lifecycle; widgets get registered/disposed unpredictably | Call `start()` once after all initial widgets are added; mount more later via `addWidgets` |
| Building search by hitting `algoliasearch` directly | Bypasses widget tree, routing, and analytics | Use widgets via `search.addWidgets([...])` |
| Hardcoding an index name like `instant_search` | Demo index from training data | Ask the user |
| Guessing widget options (`refinementList({ searchable, showMore, ... })`) | Option names and accepted values change between versions | Read the widget's `.d.ts` from `node_modules/instantsearch.js/es/widgets/<widget>` before using |
| Guessing `ais-*` class names | CamelCase, easy to miss | Grep `node_modules/instantsearch.js` for the actual class |
| Re-implementing `pagination`, `refinementList`, `sortBy` with connectors and DOM | Loses URL sync, accessibility, and the widget's edge-case handling | Use the widget; reach for the connector only when rendering genuinely cannot use templates |
| Mixing autocomplete with `searchBox` to build search-as-you-type | Two state owners; flaky | Use `@algolia/autocomplete-js` for autocomplete dropdowns; `searchBox` only for results-page input |
| Skipping `templates` / `cssClasses` and styling via DOM-position selectors | Brittle, breaks on minor version updates | Use the widget's `templates` and `cssClasses` options; target `ais-*` classes in CSS |
| Forgetting to `dispose` widgets you removed dynamically | Search keeps refining for the removed widget; URL state stays | Use `search.removeWidgets([...])`; reach for the dispose method on the widget if needed |
13 changes: 13 additions & 0 deletions skills/instantsearch/references/js/autocomplete/anti-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Autocomplete Anti-patterns (InstantSearch.js)

These are in addition to the [JS anti-patterns](../anti-patterns.md). Note: some anti-patterns from the React skill **do not apply** in vanilla JS because the recommended path is different.

| Anti-pattern | Why it's wrong | What to do instead |
| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| Building autocomplete with `searchBox` + `hits` and a custom dropdown | Reinvents `@algolia/autocomplete-js` poorly; loses keyboard nav, A11y, plugins | Use `@algolia/autocomplete-js` per [features.md](features.md) |
| Forgetting `instance.destroy()` on SPA route change / teardown | Leaks DOM and event listeners; double-mount on hot reload causes duplicate panels | Save the `autocomplete(...)` return value and call `destroy()` at teardown |
| Recreating the `searchClient` for every page render | Loses the request cache; thrashes network | Declare `searchClient` in module scope and import it |
| Recreating the `autocomplete(...)` instance on every reactive update | Tears down and rebuilds the dropdown; flicker | Mount once; update via `instance.setQuery`, `setActiveItemId`, or option closures |
| Guessing `getSources` shape from training data | The shape evolves; `getItems` return values, `templates`, plugin contracts change | Read installed `@algolia/autocomplete-js` types and the live doc before writing |
| Mixing `instantsearch({...})` widgets and `@algolia/autocomplete-js` over the same input | Two state owners on one input; flaky behavior | Pick one: autocomplete-js for the dropdown experience, `searchBox` for results-page input |
| Styling `ais-*` classes when the rendered DOM is autocomplete-js (`aa-*`) | Selectors don't match; styles silently noop | Style `aa-*` classes for autocomplete-js panels; `ais-*` only for InstantSearch widgets |
Loading