{"owner":"algolia","repo":"instantsearch","hasSkills":true,"totalSkillsCount":43,"totalTokensCount":43884,"categories":["command-prompt","anthropic-skill","root-instruction","claude-rule","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":[".claude/commands/expose-option.md",".claude/commands/preflight.md",".claude/rules/e2e.md",".claude/skills/port-widget/SKILL.md",".claude/skills/port-widget/agents/openai.yaml",".claude/skills/port-widget/references/js-flavor.md",".claude/skills/port-widget/references/react-flavor.md",".claude/skills/port-widget/references/testing.md",".claude/skills/port-widget/references/vue-flavor.md","AGENTS.md","CLAUDE.md","packages/algoliasearch-helper/CLAUDE.md","packages/algoliasearch-helper/README.md","packages/algoliasearch-helper/examples/readme.md","packages/create-instantsearch-app/README.md","packages/create-instantsearch-app/docs/custom-templates.md","packages/create-instantsearch-app/docs/deploy.md","packages/create-instantsearch-app/src/templates/Autocomplete/README.md","packages/create-instantsearch-app/src/templates/InstantSearch.js widget/README.md","packages/create-instantsearch-app/src/templates/InstantSearch.js/README.md","packages/create-instantsearch-app/src/templates/JavaScript Client/README.md","packages/create-instantsearch-app/src/templates/JavaScript Helper/README.md","packages/create-instantsearch-app/src/templates/React InstantSearch Native/README.md","packages/create-instantsearch-app/src/templates/React InstantSearch/README.md","packages/create-instantsearch-app/src/templates/Vue InstantSearch with Vue 3/README.md","packages/create-instantsearch-app/src/templates/Vue InstantSearch/README.md","packages/instantsearch-codemods/README.md","packages/instantsearch-ui-components/CLAUDE.md","packages/instantsearch-ui-components/README.md","packages/instantsearch-ui-components/src/lib/markdown-to-jsx/README.md","packages/instantsearch.css/README.md","packages/instantsearch.js/CLAUDE.md","packages/instantsearch.js/README.md","packages/instantsearch.js/scripts/transforms/README.md","packages/react-instantsearch-core/README.md","packages/react-instantsearch-nextjs/CLAUDE.md","packages/react-instantsearch-nextjs/README.md","packages/react-instantsearch-router-nextjs/CLAUDE.md","packages/react-instantsearch-router-nextjs/README.md","packages/react-instantsearch/CLAUDE.md","packages/react-instantsearch/README.md","packages/vue-instantsearch/CLAUDE.md","packages/vue-instantsearch/README.md"],"skills":{".claude/commands/expose-option.md":"---\ndescription: Thread a new option on an existing connector through all three flavors (JS, React, Vue) + common tests\nargument-hint: <connector> <optionName> [short description of what it does]\n---\n\nYou are exposing a new option through the InstantSearch fan-out. The architecture is **connector (logic) → flavor wrapper → shared UI**, so the option is defined once in the connector and surfaced by each flavor.\n\n**Input:** `$ARGUMENTS`\n\n- First token = the connector (e.g. `connectRefinementList` or just `refinementList`).\n- Second token = the new option name (e.g. `clearOnChange`).\n- Remaining text = a short description of the behavior, if given.\n\nIf the connector or option name is missing or ambiguous, ask before changing files. If the behavior isn't specified, ask what the option should do — do **not** guess at semantics.\n\nThis is a checklist for a cross-flavor option rollout. The connector is the single source of truth; React and Vue only _wrap_ it, so do the connector first, then the wrappers.\n\n## Steps\n\n1. **Scope the contract first.** Read the connector `packages/instantsearch.js/src/connectors/<name>/connect<Pascal>.ts` and its JS widget `packages/instantsearch.js/src/widgets/<name>/<name>.tsx`. Confirm the option doesn't already exist and settle the exact semantics + the option's type signature. This is the contract every flavor must match.\n\n2. **Connector first** (in `packages/instantsearch.js`):\n   - Add the option to the connector's widget-params type with a TSDoc comment.\n   - Implement the behavior in the lifecycle (`init`/`render`/`getWidgetSearchParameters`/etc.).\n   - Surface it on the **typed render state** if consumers need it (untyped render state is an oxlint error).\n   - Thread it through the JS widget. Keep it framework-agnostic — no React/Vue/DOM-framework code in the connector.\n\n   For a presentational-only variant, **reuse the existing connector** with a distinct `$$widgetType` — never fork connector logic.\n\n3. **If the option changes shared markup/layout** (not just behavior), update the shared `create<Name>Component` in `packages/instantsearch-ui-components` _before_ wiring the flavors — its output is the contract they consume.\n\n4. **Thread it through the wrappers** (independent of each other once the connector is settled):\n   - **React** — the hook `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts` and the component `packages/react-instantsearch/src/widgets/<Pascal>.tsx`, kept in sync and typed.\n   - **Vue** — `packages/vue-instantsearch/src/components/<Pascal>.{vue,js}` (and `src/mixins/` if wiring lives there), working for **both Vue 2 and Vue 3**.\n\n5. **Tests.** Cross-flavor behavior → add/extend `tests/common/connectors/<name>/` (or `.../widgets/<name>/`) and register it in each flavor's `common-*.test.*` (see `tests/common/README.md`); flavor-specific assertions stay in each package's co-located `__tests__/`. If browser-level behavior is affected, add an e2e spec (see `.claude/rules/e2e.md`).\n\n6. **Verify.** Confirm all three flavors expose the same option with consistent naming/types, then:\n   ```bash\n   yarn jest packages/instantsearch.js/src/connectors/<name>\n   yarn jest <the react/vue paths you touched>\n   yarn type-check\n   yarn lint:changed\n   ```\n   (Or run `/preflight`.)\n\n## Notes\n\n- Why this order: the connector is the contract; React and Vue only _wrap_ it, so they're independent of each other and safe to do once the connector is settled.\n- Don't hand-edit changelogs (Ship.js generates them). Commit message: `feat(<widget>): add <optionName> option`.\n",".claude/commands/preflight.md":"---\ndescription: Run the pre-push checklist — lint, types, relevant tests, and a Conventional-Commit sanity check\nallowed-tools: Bash(yarn lint:changed), Bash(yarn lint:fix), Bash(yarn type-check), Bash(yarn type-check:*), Bash(yarn jest:*), Bash(git status:*), Bash(git diff:*), Bash(git log:*)\n---\n\nRun the InstantSearch pre-push checks against the **current changes** and report a concise pass/fail summary. Don't fix anything silently — surface failures and propose fixes.\n\n## Steps\n\n1. **Scope the change.** `git status` + `git diff --name-only` (and vs. the base branch if on a feature branch) to see which packages/files are touched.\n\n2. **Lint the changed files:** `yarn lint:changed`. If it fails, show the violations; offer `yarn lint:fix` for auto-fixable ones (note that oxlint bans `for-in`/`for-of`/`async` and implicit `any` in library code — those need manual fixes).\n\n3. **Type-check:** `yarn type-check`. If any touched code interacts with the legacy algoliasearch versions, also run `yarn type-check:v3` / `yarn type-check:v4`.\n\n4. **Run the relevant tests** — not the whole suite. Map changed files to their `yarn jest <path>` (e.g. a connector → `yarn jest packages/instantsearch.js/src/connectors/<name>`; a React widget → its `__tests__` path). If a connector's cross-flavor behavior changed, include the matching `tests/common/` path.\n\n5. **Conventional-Commit sanity check.** Look at staged changes / recent commits and confirm the message fits `type(scope): description` (scope = widget/connector or topic like `deps`/`ci`). Flag if it doesn't; suggest a corrected message. Reference issues with `fix #1234` in the body when applicable.\n\n## Output\n\nReport a short checklist:\n\n- ✅/❌ lint:changed\n- ✅/❌ type-check (+ v3/v4 if run)\n- ✅/❌ tests (list the paths run)\n- ✅/❌ commit message\n\nFor any ❌, show the failing output and the smallest fix. Don't run the full `yarn test` or `yarn build` unless asked — this is the fast pre-push loop.\n\nRun the checks directly. If a check fails, fix it in the package that owns the failing file before re-running.\n",".claude/rules/e2e.md":"---\npaths:\n  - 'tests/e2e/**'\n  - '.github/workflows/e2e.yml'\n  - '.github/workflows/ci.yml'\n  - 'website/serve.json'\n  - 'examples/*/e-commerce*/**'\n  - 'packages/react-instantsearch-nextjs/playwright.config.ts'\n  - 'packages/react-instantsearch-nextjs/__tests__/e2e/**'\n  - 'packages/react-instantsearch-router-nextjs/playwright.config.ts'\n  - 'packages/react-instantsearch-router-nextjs/__tests__/e2e/**'\n---\n\n# E2E Testing Guide\n\n## Test Locations and Frameworks\n\n| Location | Framework | Purpose |\n| --- | --- | --- |\n| `tests/e2e/playwright/` | Playwright | Main e2e suite for all InstantSearch flavors |\n| `tests/e2e/` (wdio files) | WebdriverIO | IE11 tests only (via Sauce Labs) |\n| `packages/react-instantsearch-nextjs/__tests__/e2e/` | Playwright | App Router Next.js e2e tests |\n| `packages/react-instantsearch-router-nextjs/__tests__/e2e/` | Playwright | Pages Router Next.js e2e tests |\n\n`.github/workflows/e2e.yml` runs on a weekday schedule and is called from `ci.yml` on pushes to master, never on pull requests. Run the suites locally before you push.\n\n## Running Tests Locally\n\n### Main E2E Suite (Playwright)\n\n**Prerequisites:** Examples must be built first:\n\n```bash\nyarn website:examples\n```\n\n```bash\n# Run all Playwright tests (all flavors, chromium + firefox)\nyarn test:e2e\n\n# Run tests for a specific flavor\nE2E_FLAVOR=react yarn test:e2e\n\n# Run tests for a specific browser (faster local dev)\nE2E_BROWSER=chromium yarn test:e2e\n\n# Combine both for fastest local iteration\nE2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n\n# Run tests with UI mode for debugging\nyarn workspace @instantsearch/e2e-tests test:playwright --ui\n```\n\n### Next.js Package Tests\n\n```bash\n# App Router (react-instantsearch-nextjs)\nyarn workspace react-instantsearch-nextjs test:e2e\n\n# Pages Router (react-instantsearch-router-nextjs)\nyarn workspace react-instantsearch-router-nextjs test:e2e\n\n# Run with list reporter (outputs to terminal instead of opening browser)\nyarn workspace react-instantsearch-nextjs test:e2e --reporter=list\nyarn workspace react-instantsearch-router-nextjs test:e2e --reporter=list\n```\n\n### IE11 Tests (Requires Sauce Labs credentials)\n\n```bash\n# Set environment variables first\nexport SAUCE_USERNAME=your_username\nexport SAUCE_ACCESS_KEY=your_access_key\n\nyarn test:e2e:ie11\n```\n\n## Writing Playwright Tests\n\n### Test Structure\n\n```typescript\nimport { test, expect } from '../fixtures';\n\ntest.describe('feature name', () => {\n  test('does something specific', async ({ page, helpers }) => {\n    // Use '.' to navigate to baseURL, NOT '/' (which goes to root)\n    await page.goto('.');\n\n    // Use helpers for common InstantSearch interactions\n    await helpers.clickRefinementListItem('Apple');\n    await helpers.setSearchBoxValue('macbook');\n\n    // Use Playwright's built-in assertions\n    await expect(page.locator('.ais-Hits-item')).toHaveCount(16);\n  });\n});\n```\n\n### Available Helper Methods\n\nThe `helpers` fixture provides these methods. Full signatures in `tests/e2e/playwright/fixtures.ts`.\n\n- **RefinementList:** `clickRefinementListItem(label)`, `getSelectedRefinementListItem()`\n- **SearchBox:** `setSearchBoxValue(value)`, `getSearchBoxValue()`\n- **Hits:** `getHitsTitles()`\n- **HierarchicalMenu:** `clickHierarchicalMenuItem(label)`, `getSelectedHierarchicalMenuItems()`\n- **RangeSlider:** `dragRangeSliderLowerBoundTo(value)`, `dragRangeSliderUpperBoundTo(value)`, `getRangeSliderLowerBoundValue()`, `getRangeSliderUpperBoundValue()`\n- **Pagination:** `clickPage(number)`, `clickNextPage()`, `clickPreviousPage()`, `getCurrentPage()`\n- **ToggleRefinement:** `clickToggleRefinement()`, `getToggleRefinementStatus()`\n- **RatingMenu:** `clickRatingMenuItem(label)`, `getSelectedRatingMenuItem()`. `label` is the link's `aria-label`, such as `4 & up`.\n- **SortBy:** `setSortByValue(label)`, `getSortByValue()`\n- **HitsPerPage:** `setHitsPerPage(label)`, `getHitsPerPage()`\n- **ClearRefinements:** `clickClearRefinements()`\n- **Any selector:** `waitForElement(selector)`, `getTextFromSelector(selector)`\n\n### Best Practices\n\n1. **Use helpers for InstantSearch interactions** - They handle waiting for URL changes automatically\n2. **Prefer Playwright's auto-waiting assertions** - Use `await expect(locator).toHaveText('...')` instead of manual waits\n3. **Avoid `page.waitForTimeout()`** - Use proper waits like `waitForURL()` or `expect.poll()`\n4. **Use locator chaining** - `page.locator('.parent').locator('.child')`\n5. **Use `'.'` for baseURL navigation** - `page.goto('.')` navigates to the baseURL path; `page.goto('/')` goes to the root domain (common mistake)\n6. **Prefer relative paths** - For routes like `search/category/?query=test`, use paths without leading `/`\n7. **Avoid hardcoded test data** - The Algolia index data can change. Instead of checking exact product names, verify behavior (e.g., results changed, count > 0, URL updated)\n8. **Wait for results after navigation** - After pagination or filter changes, wait for new results to load before asserting\n9. **Don't use `scrollIntoViewIfNeeded()`** - Playwright auto-scrolls elements into view before clicking\n10. **Don't add redundant waits after URL waits** - If you wait for URL change, the UI has already updated; no need to also wait for element visibility\n\n## Flavor System\n\nThe main e2e suite tests multiple InstantSearch flavors:\n\n- `js` - instantsearch.js with vanilla JS\n- `js-umd` - instantsearch.js UMD build\n- `react` - React InstantSearch\n- `vue` - Vue InstantSearch\n\nTests run against example apps in `website/examples/{flavor}/e-commerce/`.\n\nTo run a single flavor:\n\n```bash\nE2E_FLAVOR=react yarn test:e2e\n```\n\n## IE11 Considerations\n\nIE11 tests are kept in WebdriverIO because Playwright doesn't support IE11. These tests:\n\n- Only run `js` and `js-umd` flavors\n- Require Sauce Labs for remote IE11 browser\n- Use the same test specs in `tests/e2e/specs/` and helpers in `tests/e2e/helpers/`\n\n## Debugging\n\n### Playwright\n\n```bash\n# Run with headed browser\nyarn workspace @instantsearch/e2e-tests test:playwright --headed\n\n# Run with UI mode (interactive debugging)\nyarn workspace @instantsearch/e2e-tests test:playwright --ui\n\n# Run specific test file\nyarn workspace @instantsearch/e2e-tests test:playwright specs/brand-and-query.spec.ts\n\n# Debug mode with inspector\nPWDEBUG=1 yarn workspace @instantsearch/e2e-tests test:playwright\n```\n\n### View Test Reports\n\nAfter running tests, open the HTML report:\n\n```bash\nnpx playwright show-report tests/e2e/playwright/playwright-report\n```\n\n## Troubleshooting\n\n### Tests timeout waiting for elements\n\n1. **Check if examples are built**: Run `yarn website:examples` before running tests\n2. **Check if the server starts**: The webServer uses port 3456 (not 5000, which conflicts with macOS AirPlay)\n3. **Check navigation**: Use `page.goto('.')` not `page.goto('/')` to navigate to baseURL\n\n### Port conflicts on macOS\n\nOn macOS, port 5000 is used by AirPlay Receiver (ControlCenter). The tests use port 3456 instead. If you need to change the port, update:\n\n- `tests/e2e/playwright/playwright.config.ts` (both `baseURL` and `webServer.command/url`)\n\n### SPA routing not working\n\nThe e-commerce examples use History API routing (e.g., `/search/Appliances/`). The `website/serve.json` file configures rewrites for these routes. If you add new SPA routes, update this file:\n\n```json\n{\n  \"rewrites\": [\n    {\n      \"source\": \"/examples/js/e-commerce/search/**\",\n      \"destination\": \"/examples/js/e-commerce/index.html\"\n    }\n  ]\n}\n```\n\n### Tests are flaky with multiple workers\n\nThe static server can handle 2 parallel workers. All configs use 2 workers for both CI and local runs.\n\nThe main suite retries once locally and twice in CI. The Next.js suites retry twice in CI and not at all locally.\n\n### Multiple elements matched\n\nIf you see \"strict mode violation\" errors about multiple elements:\n\n- SearchBox selectors are scoped to the header to avoid matching RefinementList search inputs\n- Use more specific selectors when targeting widgets that may appear multiple times\n\n### Different HTML structures across flavors\n\nThe JS and React examples have different HTML structures:\n\n- **JS examples**: Use `id=\"header\"` and `data-widget=\"searchbox\"` attributes\n- **React examples**: Use `className=\"header\"` (class instead of id) and render SearchBox directly inside header\n\nThe fixtures account for this with combined selectors like:\n\n```typescript\n'#header .ais-SearchBox [type=search], .header > .ais-SearchBox [type=search], [data-widget=\"searchbox\"] .ais-SearchBox [type=search]';\n```\n\nIf you add new selectors, ensure they work across all flavors.\n\n## Migration Notes: WebDriverIO to Playwright\n\nWhen migrating tests from WebDriverIO to Playwright, be aware of these differences:\n\n### URL Waiting\n\n- **WebDriverIO**: Used custom `waitForUrl()` that polls `browser.getUrl()` until match\n- **Playwright**: Use `await expect(page).toHaveURL(url)` which has built-in retrying\n\n### Element Selection\n\n- **WebDriverIO**: `$('.ais-Hits-item')` returns first match, clicking works on container elements\n- **Playwright**: `page.locator('.ais-Hits-item').first()` - clicking on container may not hit child link elements. Use `.locator('a')` to target links explicitly:\n\n  ```typescript\n  // WebDriverIO\n  const link = await $('.ais-Hits-item');\n  await link.click();\n\n  // Playwright - click the link inside the hit\n  const link = page.locator('.ais-Hits-item a').first();\n  await link.click();\n  ```\n\n### Browser Back Navigation\n\n- **WebDriverIO**: `browser.back()` worked reliably for SPA navigation\n- **Playwright**: `page.goBack()` works but may require `slowMo` in headless mode for Next.js App Router dynamic routes\n\n### Headless Mode Timing Issues\n\nPlaywright's headless mode runs faster than headed mode, which can cause timing issues with SPA navigation and state updates. For Next.js App Router tests involving browser back/forward navigation with dynamic routes, add `slowMo` at the test file level (not globally, to avoid slowing down all tests):\n\n```typescript\nimport { test, expect } from '@playwright/test';\n\n// slowMo is required for browser back/forward navigation tests in headless mode\ntest.use({ launchOptions: { slowMo: 500 } });\n\ntest.describe('browser back/forward buttons', () => {\n  // tests that use page.goBack() or page.goForward()\n});\n```\n\nThis gives the browser enough time to process JavaScript events like popstate handlers used by InstantSearch routing, while keeping other tests fast.\n",".claude/skills/port-widget/SKILL.md":"---\nname: port-widget\ndescription: Port or introduce an InstantSearch widget or connector-driven feature across this monorepo's JavaScript, React, and Vue flavors. Use when the task is to add a missing wrapper around an existing connector, bring a widget from one flavor to another, or implement a new shared widget end-to-end in `instantsearch.js`, `react-instantsearch`, and `vue-instantsearch`. Triggers include requests like \"port X to vue\", \"add Y to react-instantsearch\", \"make Z available in all flavors\", or \"implement the missing InstantSearch wrapper for this connector\".\n---\n\n# Port InstantSearch Widgets Across Flavors\n\n## Start with the repo audit\n\n- Run `python3 scripts/audit_widget_coverage.py <widget-kebab-name>` from this skill folder before editing.\n- Use `--repo /path/to/instantsearch` if your current working directory is not inside the InstantSearch repo.\n- Treat placeholder Vue failures in `packages/vue-instantsearch/src/__tests__/common-widgets.test.js` or `common-connectors.test.js` as evidence that the connector exists but the Vue wrapper still needs work.\n\n## Layer map\n\n- Connector: `packages/instantsearch.js/src/connectors/<widget>/connect<Pascal>.ts`\n- JS widget: `packages/instantsearch.js/src/widgets/<widget>/<widget>.tsx`\n- React hook: `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts`\n- React widget: `packages/react-instantsearch/src/widgets/<Pascal>.tsx`\n- Optional React UI: `packages/react-instantsearch/src/ui/<Pascal>.tsx`\n- Vue wrapper: `packages/vue-instantsearch/src/components/<Pascal>.vue` or `.js`\n- Shared widget tests: `tests/common/widgets/<widget>/`\n- Shared connector tests: `tests/common/connectors/<widget>/`\n\n## Variant widgets\n\nSome widgets reuse another widget's connector with different defaults or UI. For example, `menuSelect` uses `connectMenu`/`useMenu` (not a dedicated `connectMenuSelect`). The audit will show `no` for connector and hook rows — this is expected. The `$$widgetType` still differs (`ais.menuSelect` vs `ais.menu`). When porting a variant widget, skip connector/hook creation and reuse the existing hook directly in the widget file.\n\nKnown variants: `menuSelect` → `connectMenu`/`useMenu`.\n\n## Workflow\n\n1. Decide the scope.\n   - Existing connector, missing wrapper: keep the connector API unchanged and port only the wrapper plus wrapper tests.\n   - Variant widget (shared connector, different UI): skip connector/hook creation; reuse the existing hook and set a distinct `$$widgetType`.\n   - Missing connector or changed render state: start in `instantsearch.js`, then update every flavor and both common test suites.\n   - Vue port for a newer recommendation, chat, or filter-suggestions feature: inspect `Hits.js`, `Highlighter.js`, `DynamicWidgets.js`, and `util/vue-compat.js` before designing the wrapper.\n2. Match a real precedent.\n   - Pick one close widget in the target flavor and one close widget in another flavor.\n   - Reuse the same prop names, slot or component escape hatches, `$$widgetType`, and test style.\n3. Build from the bottom up.\n   - Connector exports belong in `packages/instantsearch.js/src/connectors/index.ts`.\n   - JS widget exports belong in `packages/instantsearch.js/src/widgets/index.ts`.\n   - React hook exports belong in `packages/react-instantsearch-core/src/index.ts`.\n   - React widget exports belong in `packages/react-instantsearch/src/widgets/index.ts`; `packages/react-instantsearch/src/index.ts` already re-exports widgets.\n   - Vue exports belong in `packages/vue-instantsearch/src/widgets.js`; `src/instantsearch.js` and the plugin re-export and register from there automatically.\n4. Choose the right sharing model.\n   - JS and React: prefer `instantsearch-ui-components` when the markup can be shared.\n   - React: create `src/ui/<Pascal>.tsx` whenever the widget has no shared factory in `instantsearch-ui-components`. This includes simple widgets like `MenuSelect` (a plain `<select>`) — `src/ui/` is for all React-rendered markup, not only complex cases.\n   - Vue: use `.vue` SFCs for slot-heavy markup and `.js` render functions with `renderCompat` when reusing `instantsearch-ui-components`.\n5. Wire tests before finishing.\n   - Update `tests/common/widgets/<widget>/` whenever the wrapper behavior changes.\n   - Update `tests/common/connectors/<widget>/` whenever connector params or render state change.\n   - Register the suite in each flavor's `common-widgets.test.*` and `common-connectors.test.*`.\n   - Replace any `throw new Error('X is not supported in ...')` placeholder with real setup code in the target flavor's `common-widgets.test.*`.\n   - Remove the corresponding `skippedTests` entry in `testOptions` for that widget.\n   - For React: always add the widget to the switch in `packages/react-instantsearch/src/widgets/__tests__/__utils__/all-widgets.tsx` with the required minimum props.\n6. Check examples only when the widget is user-facing.\n   - Search existing examples first. Recommendation, chat, and query-suggestion widgets already live in getting-started or query-suggestions examples, not only the e-commerce apps.\n   - Add to `examples/*/e-commerce` only when the widget fits the shared storefront UX or existing Playwright coverage.\n\n## Reminders\n\n- Keep `$$widgetType` aligned across flavors.\n- Do not invent new Vue patterns; match `createWidgetMixin`, `createSuitMixin`, scoped slots, and `renderCompat`.\n- Do not add memoization hooks in React unless an adjacent widget uses them for the same reason.\n\n### Cross-package API migration checks\n\n- Update all affected layers in one pass when moving or splitting behavior: wrapper APIs, shared UI components, layout/type contracts, exports, and tests. Wrapper-only edits create cross-package type drift.\n- If you remove a required prop or render path, either delete it end-to-end or make downstream contracts optional in the same change; partial migrations often fail during declaration builds.\n- Before finishing, grep for legacy names (old props, options, class names, template keys, translations keys) to catch leftovers across flavors.\n- Keep control contracts explicit and consistent when behavior is externally controlled (e.g., imperative handles or widget methods), so external integrations do not depend on internal layout details.\n- Do not add ad-hoc top-level markdown change logs unless requested; keep source of truth in code, tests, and existing docs.\n- Always run monorepo build validation (`yarn build --ignore='example*'`) after cross-package API changes; it catches declaration and export drift that local tests may miss.\n\n## References\n\n- JS wrappers: [references/js-flavor.md](references/js-flavor.md)\n- React wrappers: [references/react-flavor.md](references/react-flavor.md)\n- Vue wrappers: [references/vue-flavor.md](references/vue-flavor.md)\n- Testing and examples: [references/testing.md](references/testing.md)\n",".claude/skills/port-widget/agents/openai.yaml":"interface:\n  display_name: 'Port InstantSearch Widget'\n  short_description: 'Port widgets across InstantSearch flavors'\n  default_prompt: 'Use $port-widget to port a widget or connector-driven feature across the InstantSearch JavaScript, React, and Vue packages in this repo.'\n\npolicy:\n  allow_implicit_invocation: true\n",".claude/skills/port-widget/references/js-flavor.md":"# JavaScript Flavor (`instantsearch.js`)\n\n## Reach for these precedents first\n\n- Shared UI via `instantsearch-ui-components`: `hits`, `related-products`, `trending-items`, `filter-suggestions`\n- Older Preact widget with templates and CSS helpers: `refinement-list`, `menu`, `pagination`\n\n## File layout\n\n```text\npackages/instantsearch.js/src/widgets/<widget>/\n  <widget>.tsx\n  defaultTemplates.tsx        # only when the widget exposes templates\n  __tests__/<widget>-test.tsx # optional flavor-specific tests\n```\n\n## Wrapper pattern\n\n- Import the connector from `../../connectors/<widget>/connect<Pascal>`.\n- Build a `createRenderer(...)` helper that closes over `containerNode`, classes, templates, and any normalized props.\n- Use `getContainerNode(container)` and throw via `createDocumentationMessageGenerator` when `container` is missing.\n- Mount with `render(<UiComponent ... />, containerNode)` and unmount with `render(null, containerNode)`.\n- Return `$$widgetType: 'ais.<camelName>'`.\n\n## UI choice\n\n- Prefer `instantsearch-ui-components` when the React and JS widgets should share markup.\n- Keep local JSX only when the JS widget still depends on legacy template plumbing or DOM structure that is not shared elsewhere.\n- If templates are supported, follow `TemplateComponent` and `prepareTemplateProps` patterns from a close sibling widget rather than inventing a new API.\n\n## Registration checklist\n\n- Export the connector from `packages/instantsearch.js/src/connectors/index.ts`.\n- Export the widget from `packages/instantsearch.js/src/widgets/index.ts`.\n",".claude/skills/port-widget/references/react-flavor.md":"# React Flavor (`react-instantsearch`)\n\n## Package split\n\n- `packages/react-instantsearch-core`: hooks that wrap `instantsearch.js` connectors\n- `packages/react-instantsearch`: public widgets and optional React-only UI components\n\n## Hook pattern\n\nFile: `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts`\n\n- Import the connector from `instantsearch.js/es/connectors/<widget>/connect<Pascal>`.\n- Import `useConnector` and `AdditionalWidgetProperties` from `../hooks/useConnector`.\n- Export `Use<Pascal>Props = <Pascal>ConnectorParams`.\n- Pass `additionalWidgetProperties` through to `useConnector(...)`.\n\n## Widget pattern\n\nChoose the closest precedent before writing code:\n\n- Shared UI component wrapper: `Hits.tsx`, `RelatedProducts.tsx`, `TrendingItems.tsx`, `FilterSuggestions.tsx`\n- React-only presentational UI in `src/ui`: `SearchBox.tsx`, `RangeInput.tsx`, `RefinementList.tsx`, `Menu.tsx`, `MenuSelect.tsx`, `SortBy.tsx`\n\n### If the UI is shared via `instantsearch-ui-components`\n\n- Instantiate the factory with `createElement as Pragma` and `Fragment`.\n- Keep connector params separate from UI-only props.\n- Pass `{ $$widgetType: 'ais.<camelName>' }` as the second argument to the hook.\n- Use `useInstantSearch()` only when the UI needs search status or other top-level state.\n- Only add `useMemo` or `useCallback` when an adjacent widget uses them to stabilize component props passed into a shared UI factory.\n\n### If no shared factory exists (React-only UI)\n\n- Create `packages/react-instantsearch/src/ui/<Pascal>.tsx` for the presentational component. This applies to all widgets without a shared factory — even simple ones like `MenuSelect` (a plain `<select>`).\n- Keep the widget file focused on mapping hook state and event handlers into the UI component.\n\n### Variant widgets (shared connector)\n\nSome widgets reuse another widget's hook instead of having a dedicated one. For example, `MenuSelect` uses `useMenu` (not `useMenuSelect`). In this case:\n\n- Import the existing hook directly in the widget file.\n- Pick only the relevant connector params (e.g., exclude `showMore`/`showMoreLimit` for `MenuSelect`).\n- Set a distinct `$$widgetType` (e.g., `'ais.menuSelect'`).\n\n## Registration checklist\n\n- Export the hook from `packages/react-instantsearch-core/src/index.ts` (skip for variant widgets that reuse an existing hook).\n- Export the widget from `packages/react-instantsearch/src/widgets/index.ts`.\n- If the widget is public, `packages/react-instantsearch/src/index.ts` already re-exports it through `./widgets`.\n- Add the widget to the switch in `packages/react-instantsearch/src/widgets/__tests__/__utils__/all-widgets.tsx` with required minimum props (e.g., `attribute=\"brand\"`).\n- In `packages/react-instantsearch/src/__tests__/common-widgets.test.tsx`:\n  - Replace the `throw new Error('X is not supported in React InstantSearch')` with real setup code.\n  - Remove the `skippedTests` entry for the widget in `testOptions`.\n",".claude/skills/port-widget/references/testing.md":"# Testing and Examples\n\n## Start with the common suites\n\n### Wrapper or public widget change\n\n- Update `tests/common/widgets/<widget>/`.\n- Register or unskip the suite in each flavor's `common-widgets.test.*`:\n  - `packages/instantsearch.js/src/__tests__/common-widgets.test.tsx`\n  - `packages/react-instantsearch/src/__tests__/common-widgets.test.tsx`\n  - `packages/vue-instantsearch/src/__tests__/common-widgets.test.js`\n- For previously unsupported widgets: replace the `throw new Error('X is not supported in ...')` placeholder with real setup code, and remove the `skippedTests` entry in `testOptions`.\n\n### Connector or hook API change\n\n- Update `tests/common/connectors/<widget>/`.\n- Register or unskip the suite in:\n  - `packages/instantsearch.js/src/__tests__/common-connectors.test.tsx`\n  - `packages/react-instantsearch/src/__tests__/common-connectors.test.tsx`\n  - `packages/vue-instantsearch/src/__tests__/common-connectors.test.js`\n\n## Flavor-specific tests\n\n- JavaScript: `packages/instantsearch.js/src/widgets/<widget>/__tests__/`\n- React: `packages/react-instantsearch/src/widgets/__tests__/`\n- Vue: colocated component or package tests under `packages/vue-instantsearch/src/__tests__/`\n\nAdd flavor-specific tests only for behavior that the shared suites do not cover, such as:\n\n- slot rendering differences in Vue\n- React-only UI plumbing\n- template or CSS-class edge cases in `instantsearch.js`\n- registry coverage such as React's `all-widgets` helpers\n\n## Shared cross-widget suites\n\n- Search `tests/common/shared/` and `tests/common/shared-composition/` when the port affects routing, searchable behavior, or cross-widget composition.\n- Only touch those suites when the new widget changes shared behavior rather than a single widget contract.\n\n## Examples\n\n- Search existing examples before editing; not every user-facing widget belongs in the e-commerce demos.\n- E-commerce apps live in:\n  - `examples/js/e-commerce/src/`\n  - `examples/react/e-commerce/`\n  - `examples/vue/e-commerce/src/`\n- Recommendation, chat, and query-suggestion features already appear in getting-started or query-suggestions examples for JS and React.\n\n## Vue-specific test timing\n\n### State delivery is async for some connectors\n\nMost connectors (e.g. `connectRefinementList`) deliver state synchronously during `mountApp` because the mock search client responds immediately. Newer connectors like `connectChat` deliver state asynchronously. The Vue test setup must account for this:\n\n```javascript\nmountApp({ render: ... }, container);\nawait nextTick();                                      // Vue tick\nawait new Promise((resolve) => setTimeout(resolve, 0)); // macrotask for async state\nawait nextTick();                                      // Vue tick to re-render\n```\n\n### Provide a custom `act` for Vue\n\nVue batches DOM updates asynchronously, unlike React which re-renders synchronously within event handlers. Tests that `await act(async () => { await wait(0); })` work because `act` wraps the wait. Provide a custom `act` in `testOptions` that flushes Vue's update queue:\n\n```javascript\ncreateXxxWidgetTests: {\n  act: async (cb) => {\n    await cb();\n    await nextTick();\n  },\n},\n```\n\nTests that click then immediately assert DOM changes (without `act`) will still fail in Vue. These are known limitations of the shared test suite's React-centric timing assumptions. Document them in the `testOptions` but avoid skipping the entire test suite.\n\n### Flavored test params\n\nFor `flavored` test suites (like Chat), `runTestSuites` extracts the flavor-specific params via `widgetParams[flavor]`. The Vue setup function receives only the Vue portion. Ensure the Vue params include all required connector params (e.g. `agentId`, `chat`) — empty `{}` will cause connector errors.\n\nWhen Vue params need component functions (tools with `layoutComponent`, custom `headerComponent`), use `import { h as vueH } from 'vue'` in the `.tsx` test file and return VNodes: `() => vueH('div', { class: 'foo' }, 'text')`.\n\n### skippedTests granularity\n\n`skippableDescribe` only skips entire describe blocks by name. Individual test names in `skippedTests` have no effect unless the test file uses `skippableTest`. The shared chat/options/templates/translations test files use plain `test()`, so individual test skipping is not available for Chat widget tests.\n\n## Useful commands\n\n```bash\n# Common widget/connector tests\nnpx jest packages/instantsearch.js/src/__tests__/common-widgets.test.tsx --no-coverage\nnpx jest packages/instantsearch.js/src/__tests__/common-connectors.test.tsx --no-coverage\nnpx jest packages/react-instantsearch/src/__tests__/common-widgets.test.tsx --no-coverage\nnpx jest packages/react-instantsearch/src/__tests__/common-connectors.test.tsx --no-coverage\nnpx jest packages/vue-instantsearch/src/__tests__/common-widgets.test.js --no-coverage\n\n# Run only tests matching a specific widget name\nnpx jest packages/react-instantsearch/src/__tests__/common-widgets.test.tsx --testNamePattern=\"MenuSelect\" --no-coverage\n\n# Flavor-specific tests\nnpx jest packages/react-instantsearch/src/widgets/__tests__ --no-coverage\nnpx jest packages/instantsearch.js/src/widgets/<widget> --no-coverage\n\n# Update snapshots after adding new exports\nnpx jest packages/react-instantsearch/src/widgets/__tests__ --no-coverage -u\n\n# E2E\nyarn website:examples\nE2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n```\n",".claude/skills/port-widget/references/vue-flavor.md":"# Vue Flavor (`vue-instantsearch`)\n\n## Current patterns in this repo\n\n- Slot-heavy SFCs: `RefinementList.vue`, `Menu.vue`, `Pagination.vue`\n- Render-function wrappers around shared UI components: `Hits.js`, `Highlighter.js`\n- Framework glue helpers: `mixins/widget.js`, `mixins/suit.js`, `util/vue-compat.js`\n\n## Choose the wrapper shape deliberately\n\n### Use a `.vue` SFC when\n\n- the widget exposes multiple named slots\n- the DOM structure is easiest to express in template syntax\n- the closest existing widget is already an SFC\n\n### Use a `.js` render function when\n\n- the widget reuses `instantsearch-ui-components`\n- the wrapper mostly adapts connector state into a shared UI factory\n- the widget needs `renderCompat(...)` or lower-level vnode control\n\n## Implementation rules\n\n- Import connectors from `instantsearch.js/es/connectors/index`.\n- Use `createWidgetMixin({ connector: ... }, { $$widgetType: 'ais.<camelName>' })`.\n- Use `createSuitMixin({ name: '<Pascal>' })` for BEM classes. When the widget delegates all rendering to a shared `createXxxComponent` factory, the suit mixin's `suit()` method goes unused but the `classNames` prop it provides is still convenient. Pass `this.classNames` directly to the shared component's `classNames` prop (semantic keys like `{ root, container }`, not BEM keys).\n- Expose connector params through a computed `widgetParams()` object.\n- When reusing shared UI factories, wrap the render function with `renderCompat(...)` and map `this.classNames` into the `classNames` prop expected by the shared component.\n- Prefer `getScopedSlot` or `getDefaultSlot` helpers over direct slot access when matching render-function components.\n- In render-function callbacks that reference connector state (e.g. `onSubmit`, `onInput`), read from `this.state.xxx` instead of destructured locals. Vue batches re-renders, so destructured values become stale between synchronous user interactions (type then click).\n\n## Registration checklist\n\n- Export the component from `packages/vue-instantsearch/src/widgets.js`.\n- Do not manually edit `src/plugin.js` or `src/instantsearch.js` for normal widget additions; they already derive registration and exports from `widgets.js`.\n- Search `packages/vue-instantsearch/src/__tests__/common-widgets.test.js` and `common-connectors.test.js` for placeholder \"not supported\" branches before deciding the widget is genuinely absent.\n\n## Missing-feature caution\n\n- Recommendation widgets, chat, and filter suggestions already have connector-level test placeholders in Vue, but several still throw explicit \"not supported\" errors at the widget layer.\n- If you port one of those widgets, remove the placeholder failure and replace it with real setup code plus the component implementation.\n","AGENTS.md":"CLAUDE.md","CLAUDE.md":"# InstantSearch monorepo\n\nSearch UI libraries for Algolia across three flavors (vanilla JS, React, Vue), sharing one connector + UI-component core. Yarn 4 workspaces + Lerna (independent versioning).\n\n## Repo map\n\n| Package | What it is |\n| --- | --- |\n| `packages/instantsearch.js` | **Core.** Vanilla JS library. **All connectors live here** (`src/connectors/`) and are shared by every flavor. |\n| `packages/react-instantsearch-core` | Headless React: hooks (`useSearchBox`, …) that wrap connectors. Framework-agnostic (web/RN). |\n| `packages/react-instantsearch` | React DOM widgets = hooks + UI from `instantsearch-ui-components`. |\n| `packages/vue-instantsearch` | Vue 2/3 components wrapping connectors. |\n| `packages/instantsearch-ui-components` | **Shared UI layer.** Framework-agnostic widget markup + `ais-*` classes via factory components (`create<Name>Component({ createElement, Fragment })`); reused by React directly, Vue via `renderCompat`, JS via the Preact renderer. Newer widgets get their layout here; older ones still define it in `instantsearch.js/src/components/`. |\n| `packages/algoliasearch-helper` | Low-level search-parameter/state manager underneath connectors. Mature, separately-versioned; **rarely the place for a change** — fix the connector instead. See its `CLAUDE.md`. |\n| `packages/instantsearch.css` | Default themes. |\n| `packages/react-instantsearch-nextjs` / `-router-nextjs` | Next.js App Router / Pages Router integrations. |\n| `packages/create-instantsearch-app`, `instantsearch-cli`, `instantsearch-codemods` | Scaffolding / CLI / migration tooling. |\n\nArchitecture in one line: **connector (logic, in instantsearch.js) → flavor wrapper (JS widget / React hook+component / Vue component) → shared UI components**. Change behavior in the connector; change markup in the UI layer.\n\n## Where work goes (per layer)\n\nBecause behavior lives in the connector and the flavors only wrap it, most changes have a natural home — and the per-package `CLAUDE.md` files carry each layer's conventions:\n\n| Work in… | Package |\n| --- | --- |\n| Connectors, JS widgets, runtime (`src/lib`), legacy Preact components (`src/components`), types | `packages/instantsearch.js` |\n| Shared framework-agnostic markup / `ais-*` classes | `packages/instantsearch-ui-components` |\n| React widgets/hooks + Next.js integrations | `react-instantsearch` / `react-instantsearch-core` / `*-nextjs` |\n| Vue components (Vue 2 **and** 3) | `vue-instantsearch` |\n\nFor a **cross-flavor change**, settle the contract in the connector first, then update the React and Vue wrappers, then the common tests. If the change is to **shared markup/layout** (not behavior), it lives in `instantsearch-ui-components` and the flavors consume the result — a class/structure change there ripples to all flavors and `instantsearch.css` at once.\n\n**Adding or surfacing a connector option is always cross-flavor work** — settle the contract in the connector first, then thread it through React **and** Vue, then add/extend common tests. Don't wire one flavor and stop. The **`/expose-option <connector> <option>`** command walks this recipe; **`/preflight`** runs the pre-push lint/types/tests/commit check; the `/port-widget` skill adds a brand-new widget across flavors.\n\n## Commands\n\nNode `20.19.0` (`.nvmrc`). Use Yarn (v4, pinned by `packageManager`).\n\n```bash\n# Build\nyarn build                      # all packages (lerna)\nyarn workspace <pkg> build      # single package, e.g. yarn workspace instantsearch.js build\n\n# Unit tests (Jest, jsdom)\nyarn jest <path-or-pattern>     # fastest loop: run one file/pattern at root\nyarn jest packages/<pkg>        # one package's suite (flavor pkgs have no `test` script — scope jest by path)\nyarn jest common-widgets        # shared cross-flavor widget suites (JS+React+Vue); -connectors for connectors\nyarn jest common-widgets -t \"Chat widget common tests\"   # scope to one widget (label = \"<Widget> widget common tests\")\nyarn test                       # everything (slow)\n\n# Lint (oxlint) + format (oxfmt) + types\nyarn lint:changed               # lint only changed files — use this while iterating\nyarn lint:fix                   # auto-fix\nyarn format                     # format the repo; yarn format:check only reports\nyarn type-check                 # tsc + per-package (also :v3 / :v4 for legacy algoliasearch)\n\n# E2E — see .claude/rules/e2e.md (Playwright). Examples must be built first:\nyarn website:examples && E2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n```\n\n## Conventions\n\n- **TypeScript strict.** No implicit any; unused vars/params error. Avoid `for-in`/`for-of` and `async` in library code (oxlint enforces).\n- **Tests** co-locate in `__tests__/` next to source — Jest picks up _any_ file under `__tests__/` (the default `testMatch`), so some legacy tests are plain `*.js`; name new ones `*.test.ts(x)`. Prefer focused assertions; use inline snapshots sparingly (initial render only). Mocks/helpers come from `@instantsearch/mocks` and `@instantsearch/testutils`.\n- **Cross-flavor tests** live in `tests/common/{widgets,connectors}/<name>/` and each flavor registers them in its `common-widgets.test.*` / `common-connectors.test.*`. Run with `yarn jest common-widgets` (all three flavors) and scope to one widget via `-t \"<Widget> widget common tests\"`. This is the primary test surface for newer widgets like Autocomplete and Chat. See `tests/common/README.md`.\n- **Commits: Conventional Commits** — `type(scope): description`, scope = widget/connector or topic (`deps`, `ci`). e.g. `fix(searchbox): increase magnifying glass size`, `feat(hits): add custom rendering`. Reference issues with `fix #1234` in the body.\n- **Branches:** target `master`; `vX` branches are critical-fix-only. Branch names: `fix/<issue>`, `feat/<name>`.\n- **Releases** are automated via Ship.js (`yarn release`); changelogs are generated from commits — don't hand-edit them.\n- **Agent workflows that write to another repo** (`.github/workflows/docs-*.yml`) must run with the working directory _inside_ that repo. A `CLAUDE.md`/`AGENTS.md` in a workspace subdirectory is never loaded, so the target repo's conventions get silently ignored — reach the source of this repo via `--add-dir` instead. Same for `--allowedTools`: `Bash(git diff:*)` matches, `Bash(git diff *)` matches nothing, and every denied call burns a turn.\n\n## Keep these docs alive\n\nIf you discover something durable and non-obvious while working — a gotcha that cost you a debugging detour, a convention, a non-obvious file location, a cross-flavor constraint — **propose** adding it to the right doc (package `CLAUDE.md` if package-specific, this file if repo-wide, `.claude/rules/e2e.md` for e2e). Surface the proposed edit for the user to approve rather than editing silently. Bar: it must generalize beyond the current task (same bar as a good code comment) and not already be documented.\n\n## Reference docs (read before relevant work — don't duplicate here)\n\n- `CONTRIBUTING.md` — full process, commit/branch rules, package-import flow.\n- `.claude/rules/e2e.md` — E2E testing (Playwright helpers, flavors, debugging).\n- `.claude/skills/port-widget/` — adding a widget across all three flavors (the `/port-widget` skill).\n- `tests/common/README.md` — shared cross-flavor test architecture.\n- Per-package `CLAUDE.md` in `instantsearch.js`, `instantsearch-ui-components`, `react-instantsearch`, `vue-instantsearch` for package-specific layout.\n","packages/algoliasearch-helper/CLAUDE.md":"# algoliasearch-helper\n\nThe **low-level search-state manager** that sits _underneath_ InstantSearch connectors. It's a separately-published, long-stable npm package (`algoliasearch-helper`, own semver — currently 3.x) that wraps the `algoliasearch` JS client with a higher-level, stateful API: it tracks search parameters, builds the actual Algolia queries, parses responses, and emits events. Every connector in `instantsearch.js` reads and writes this helper's state — but the connector is the layer you change, not this one.\n\n> **⚠️ Rarely the right place for a change/fix/update.** This package is mature and broadly depended on — InstantSearch (all flavors) plus other Algolia front-end libraries build on it. Almost all widget/connector behavior is implemented _on top of_ the helper, not in it. Touch it **only** for a genuine state-manager or response-parsing bug, or to add a real new search-parameter primitive. Any change here is a **public-API, independently-versioned** change with a blast radius far beyond this repo — flag it explicitly and prefer fixing the connector instead.\n\n## What's in it\n\nEntry: `algoliasearchHelper(client, index, opts)` → an `AlgoliaSearchHelper` (an `EventEmitter`). Core pieces under `src/`:\n\n- **`SearchParameters/`** — the **immutable** query state (query, `facets`/`disjunctiveFacets`/`hierarchicalFacets`, numeric & tag refinements, `page`, etc.). Setters return a _new_ instance; never mutate in place. This is the object connectors mutate via `getWidgetSearchParameters`.\n- **`SearchResults/`** — parses the raw Algolia response into a richer structure: facet values, `facets_stats`, and hierarchical facet trees (`generate-hierarchical-tree.js`).\n- **`requestBuilder.js`** — turns `SearchParameters` into the actual query payload(s); notably **splits disjunctive faceting into multiple queries**.\n- **`DerivedHelper/`** — sub-requests derived from a main helper (federated / multi-index search); this is what powers the `index` widget.\n- **`RecommendParameters/` + `RecommendResults/`** — the equivalent state/results pair for the Recommend API.\n- **`functions/`** — small internal lodash-style utilities (`merge`, `omit`, `orderBy`, …).\n\nEvents emitted by the helper (what InstantSearch's lifecycle listens to): `change`, `search`, `result`, `error`, `fetch`, `searchOnce`, `searchForFacetValues`, `searchQueueEmpty`, `recommendQueueEmpty`.\n\n## Conventions that differ from the rest of the monorepo\n\n- **Plain CommonJS JavaScript**, not the TS-strict source the other packages use. Public types are **hand-written** in `index.d.ts` — update them by hand if you change the surface.\n- Has its own **`README.md` + `CHANGELOG.md`** (don't fold into the monorepo's). Build is Rollup (`yarn workspace algoliasearch-helper build`); tests run via its own `scripts/test.sh` (`yarn workspace algoliasearch-helper test`).\n","packages/algoliasearch-helper/README.md":"_Coming from V1 (or js client v2)?_ Read the [migration guide](https://github.com/algolia/algoliasearch-helper-js/wiki/Migration-guide-:-V1-to-V2) to the new version of the Helper.\n\n_Coming from V2?_ Read the [migration guide](https://github.com/algolia/algoliasearch-helper-js/wiki/Migration-guide-:-V2-to-V3) to the new version of the Helper.\n\n**The JavaScript helper is an advanced library we provide to our users. If you are looking to build a complete search interface, we recommend you to use [InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/). If you want to build an autocomplete menu, see [Autocomplete](https://github.com/algolia/autocomplete).**\n\n# algoliasearch-helper\n\nThis module is the companion of the [algolia/algoliasearch-client-javascript](https://github.com/algolia/algoliasearch-client-javascript). It helps you keep track of the search parameters and provides a higher level API.\n\n[See the helper in action](https://community.algolia.com/algoliasearch-helper-js/)\n\n[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Downloads][downloads-image]][downloads-url] [![jsDelivr Hits][jsdelivr-badge]][jsdelivr-hits]\n\n[license-image]: http://img.shields.io/badge/license-MIT-green.svg?style=flat-square\n[license-url]: LICENSE\n[downloads-image]: https://img.shields.io/npm/dm/algoliasearch-helper.svg?style=flat-square\n[downloads-url]: http://npm-stat.com/charts.html?package=algoliasearch-helper\n[version-svg]: https://img.shields.io/npm/v/algoliasearch-helper.svg?style=flat-square\n[package-url]: https://npmjs.org/package/algoliasearch-helper\n[jsdelivr-badge]: https://data.jsdelivr.com/v1/package/npm/algoliasearch-helper/badge\n[jsdelivr-hits]: https://www.jsdelivr.com/package/npm/algoliasearch-helper\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Features](#features)\n- [Examples](#examples)\n  - [Vanilla JavaScript](#vanilla-javascript)\n  - [AngularJS module](#angularjs-module)\n- [Helper cheatsheet](#helper-cheatsheet)\n  - [Add the helper in your project](#add-the-helper-in-your-project)\n  - [Regular `<script>` tag](#regular-script-tag)\n  - [With npm](#with-npm)\n  - [Init the helper](#init-the-helper)\n  - [Helper lifecycle](#helper-lifecycle)\n  - [Objects](#objects)\n  - [Search](#search)\n  - [Events](#events)\n  - [Query](#query)\n  - [Filtering results](#filtering-results)\n  - [Facet utilities](#facet-utilities)\n  - [Tags](#tags)\n  - [Pagination](#pagination)\n  - [Index](#index)\n  - [One time query](#one-time-query)\n  - [Query parameters](#query-parameters)\n  - [Results format](#results-format)\n- [Browser support](#browser-support)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Features\n\n- Search parameters management\n- Facets exclusions\n- Pagination\n- Disjunctive faceting (search on two or more values of the same facet)\n\n## Examples\n\n### Vanilla JavaScript\n\nA small example that uses Browserify to manage modules.\n\n```js\nvar algoliasearch = require('algoliasearch');\nvar algoliasearchHelper = require('algoliasearch-helper');\n\nvar client = algoliasearch('appId', 'apiKey');\n\nvar helper = algoliasearchHelper(client, 'indexName', {\n  facets: ['mainCharacterFirstName', 'year'],\n  disjunctiveFacets: ['director'],\n});\n\nhelper.on('result', function (event) {\n  console.log(event.results);\n});\n\nhelper.addDisjunctiveFacetRefinement('director', 'Clint Eastwood');\nhelper.addDisjunctiveFacetRefinement('director', 'Sofia Coppola');\n\nhelper.addNumericRefinement('year', '=', 2003);\n\n// Search for any movie filmed in 2003 and directed by either C. Eastwood or S. Coppola\nhelper.search();\n```\n\nSee more examples in the [examples folder](examples/)\n\n### AngularJS module\n\n```js\n<script src=\"https://cdn.jsdelivr.net/algoliasearch/3/algoliasearch.angular.js\"></script>\n<script src=\"dist/algoliasearch.helper.min.js\"></script>\n\n<script type=\"text/javascript\">\nangular.module('searchApp', ['ngSanitize', 'algoliasearch'])\n.controller('searchController', ['$scope', '$sce', 'algolia', function($scope, $sce, algolia) {\n  var algolia = algolia.Client('applicationId', 'apiKey');\n  $scope.q = '';\n  $scope.content = null;\n  $scope.helper = algoliasearchHelper(algolia, 'indexName', {\n    facets: ['type', 'shipping'],\n    disjunctiveFacets: ['category', 'manufacturer'],\n    hitsPerPage: 5,\n  });\n  $scope.helper.on('result', function(event) {\n    $scope.$apply(function() {\n      $scope.content = event.results;\n    });\n  });\n  $scope.toggleRefine = function($event, facet, value) {\n    $event.preventDefault();\n    $scope.helper.toggleRefine(facet, value).search();\n  };\n  $scope.$watch('q', function(q) {\n    $scope.helper.setQuery(q).search();\n  });\n  $scope.helper.search();\n}]);\n</script>\n```\n\nYou can see the full [Angular example here](https://codepen.io/Algolia/pen/PzZobK)\n\n## Helper cheatsheet\n\n[There is also a complete JSDoc](https://community.algolia.com/algoliasearch-helper-js/reference.html)\n\n### Add the helper in your project\n\n### Regular `<script>` tag\n\nUse our [jsDelivr](http://www.jsdelivr.com/) build:\n\n`<script src=\"https://cdn.jsdelivr.net/algoliasearch.helper/2/algoliasearch.helper.min.js\"></script>`\n\n### With npm\n\n`npm install algoliasearch-helper`\n\n### Init the helper\n\n```js\nvar helper = algoliasearchHelper(client, 'indexName' /*, parameters*/);\n```\n\n### Helper lifecycle\n\n1. modify the parameters of the search (usually through user interactions)<br/> `helper.setQuery('iphone').addFacetRefinement('category', 'phone')`\n\n2. trigger the search (after all the modification have been applied)<br/> `helper.search()`\n\n3. read the results (with the event \"result\" handler) and update the UI with the results<br/> `helper.on('result', function(event) { updateUI(event.results); });`\n\n4. go back to 1\n\n### Objects\n\n**AlgoliasearchHelper**: the helper. Keeps the state of the search, makes the queries and calls the handlers when an event happen.\n\n**SearchParameters**: the object representing the state of the search. The current state is stored in `helperInstance.state`.\n\n**SearchResults**: the object in which the Algolia answers are transformed into. This object is passed to the result event handler. An example of SearchResults in JSON is available at [the end of this readme](#results-format)\n\n### Search\n\nThe search is triggered by the `search()` method.\n\nIt takes all the previous modifications to the search and uses them to create the queries to Algolia. The search parameters are immutable.\n\nExample:\n\n```js\nvar helper = algoliasearchHelper(client, indexName);\n\n// Let's monitor the results with the console\nhelper.on('result', function (event) {\n  console.log(event.results);\n});\n\n// Let's make an empty search\n// The results are all sorted using the dashboard configuration\nhelper.search();\n\n// Let's search for \"landscape\"\nhelper.setQuery('landscape').search();\n\n// Let's add a category \"photo\"\n// Will make a search with \"photo\" tag and \"landscape\" as the query\nhelper.addTag('photo').search();\n```\n\n### Events\n\nThe helper is a Node.js [EventEmitter](https://nodejs.org/api/events.html#events_class_events_eventemitter) instance.\n\n`result`: get notified when new results are received. The handler function will receive two objects (`SearchResults` and `SearchParameters`).\n\n`error`: get notified when errors are received from the API.\n\n`change`: get notified when a property has changed in the helper\n\n`search` : get notified when a request is sent to Algolia\n\n#### Listen to the `result` event\n\n```js\nhelper.on('result', updateTheResults);\n```\n\n#### Listen to a `result` event once\n\n```js\nhelper.once('result', updateTheResults);\n```\n\n#### Remove a `result` listener\n\n```js\nhelper.removeListener('result', updateTheResults);\n```\n\n#### Remove all `result` listeners\n\n```js\nhelper.removeAllListeners('result');\n```\n\nAll the methods from Node.js [EventEmitter](https://nodejs.org/api/events.html#events_class_events_eventemitter) class are available.\n\n### Query\n\n#### Do a search with the query \"fruit\"\n\n```javscript\nhelper.setQuery('fruit').search();\n```\n\n### Filtering results\n\nFacets are filters to retrieve a subset of an index having a specific value for a given attribute. First you need to define which attribute will be used as a facet in the dashboard: [https://www.algolia.com/explorer#?tab=display](https://www.algolia.com/explorer#?tab=display)\n\n#### Regular (conjunctive) facets\n\nRefinements are ANDed by default (Conjunctive selection).\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  facets: ['ANDFacet'],\n});\n```\n\n##### Add a facet filter\n\n```js\nhelper.addFacetRefinement('ANDFacet', 'valueOfANDFacet').search();\n```\n\n##### Remove a facet filter\n\n```js\nhelper.removeFacetRefinement('ANDFacet', 'valueOfANDFacet').search();\n```\n\n#### Disjunctive facets\n\nRefinements are ORed by default (Disjunctive selection).\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  disjunctiveFacets: ['ORFacet'],\n});\n```\n\n##### Add a facet filter\n\n```js\nhelper.addDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();\n```\n\n##### Remove a facet filter\n\n```js\nhelper.removeDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();\n```\n\n#### Negative facets\n\nFilter so that we do NOT get a given facet\n\n##### Facet definition (same as \"AND\" facet)\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  facets: ['ANDFacet'],\n}).search();\n```\n\n##### Exclude a value for a facet\n\n```js\nhelper.addFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');\n```\n\n##### Remove an exclude from the list of excluded values\n\n```js\nhelper.removeFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');\n```\n\n#### Numeric facets\n\nFilter over numeric attributes with math operations like `=`, `>`, `<`, `>=`, `<=`. Can be used for numbers and dates (if converted to timestamp)\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  disjunctiveFacets: ['numericAttribute'],\n});\n```\n\n##### Add numeric refinements\n\n```js\nhelper.addNumericRefinement('numericAttribute', '=', '3').search();\n// filter to only the results that match numericAttribute=3\nhelper.addNumericRefinement('numericAttribute', '=', '4').search();\n// filter to only the results that match numericAttribute=3 AND numericAttribute=4\n\n// On another numeric with no previous filter\nhelper\n  .addNumericRefinement('numericAttribute2', '=', ['42', '56', '37'])\n  .search();\n// filter to only the results that match numericAttribute2=42 OR numericAttribute2=56 OR numericAttribute2=37\n```\n\n##### Remove a numeric refinement\n\n```js\nhelper.removeNumericRefinement('numericAttribute', '=', '3').search();\n```\n\n##### Batch numeric filter removal\n\n```js\n// for the single operator = on numericAttribute\nhelper.removeNumericRefinement('numericAttribute', '=').search();\n// for all the refinements on numericAttribute\nhelper.removeNumericRefinement('numericAttribute').search();\n```\n\n#### Hierarchical facets\n\nHierarchical facets are useful to build such navigation menus:\n\n```txt\n| products\n  > fruits\n    > citrus\n    | strawberries\n    | peaches\n    | apples\n```\n\nHere, we refined the search this way:\n\n- click on fruits\n- click on citrus\n\n##### Usage\n\nTo build such menu, you need to use hierarchical faceting:\n\n```javascript\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n    },\n  ],\n});\n```\n\n**Requirements:** All the specified `attributes` must be defined in your Algolia settings as attributes for faceting.\n\nGiven your objects looks like this:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": \"fruits\",\n    \"lvl1\": \"fruits > citrus\"\n  }\n}\n```\n\nAnd you refine `products`:\n\n```js\nhelper.toggleFacetRefinement('products', 'fruits > citrus');\n```\n\nYou will get a hierarchical presentation of your facet values: a navigation menu of your facet values.\n\n```js\nhelper.on('result', function (event) {\n  console.log(event.results.hierarchicalFacets[0]);\n  // {\n  //   'name': 'products',\n  //   'count': null,\n  //   'isRefined': true,\n  //   'path': null,\n  //   'data': [{\n  //     'name': 'fruits',\n  //     'path': 'fruits',\n  //     'count': 1,\n  //     'isRefined': true,\n  //     'data': [{\n  //       'name': 'citrus',\n  //       'path': 'fruits > citrus',\n  //       'count': 1,\n  //       'isRefined': true,\n  //       'data': null\n  //     }]\n  //   }]\n  // }\n});\n```\n\nTo ease navigation, we always:\n\n- provide the root level categories\n- provide the current refinement sub categories (`fruits > citrus > *`: n + 1)\n- provide the parent refinement (`fruits > citrus` => `fruits`: n -1) categories\n- refine the search using the current hierarchical refinement\n\n##### Multiple values per level\n\nYour records can also share multiple categories between one another by using arrays inside your object:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": [\"fruits\", \"color\"],\n    \"lvl1\": [\"fruits > citrus\", \"color > orange\"]\n  }\n},\n{\n  \"objectID\": \"456\",\n  \"name\": \"grapefruit\",\n  \"categories\": {\n    \"lvl0\": [\"fruits\", \"color\", \"new\"],\n    \"lvl1\": [\"fruits > citrus\", \"color > yellow\", \"new > citrus\"]\n  }\n}\n```\n\n##### Specifying another separator\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      separator: '|',\n    },\n  ],\n});\n\nhelper.toggleFacetRefinement('products', 'fruits|citrus');\n```\n\nWould mean that your objects look like so:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": \"fruits\",\n    \"lvl1\": \"fruits|citrus\"\n  }\n}\n```\n\n##### Specifying a different sort order for values\n\nThe default sort for the hierarchical facet view is: `isRefined:desc (first show refined), name:asc (then sort by name)`.\n\nYou can specify a different sort order by using:\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      sortBy: ['count:desc', 'name:asc'], // first show the most common values, then sort by name\n    },\n  ],\n});\n```\n\nThe available sort tokens are:\n\n- count\n- isRefined\n- name\n- path\n\n##### Restrict results and hierarchical values to non-root level\n\nLet's say you have a lot of levels:\n\n```\n- fruits\n  - yellow\n    - citrus\n      - spicy\n```\n\nBut you only want to get the values starting at \"citrus\", you can use `rootPath`\n\nYou can specify an root path to filter the hierarchical values\n\n```\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [{\n    name: 'products',\n    attributes: ['categories.lvl0', 'categories.lvl1', 'categories.lvl2', 'categories.lvl3'],\n    rootPath: 'fruits > yellow > citrus'\n  }]\n});\n```\n\nHaving a rootPath will refine the results on it **automatically**.\n\n##### Hide parent level of current parent level\n\nBy default the hierarchical facet is going to return the child and parent facet values of the current refinement.\n\nIf you do not want to get the parent facet values you can set showParentLevel to false\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      showParentLevel: false,\n    },\n  ],\n});\n```\n\n##### Asking for the current breadcrumb\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      separator: '|',\n    },\n  ],\n});\n\nhelper.toggleFacetRefinement('products', 'fruits|citrus');\nvar breadcrumb = helper.getHierarchicalFacetBreadcrumb('products');\n\nconsole.log(breadcrumb);\n// ['fruits', 'citrus']\n\nconsole.log(breadcrumb.join(' | '));\n// 'fruits | citrus'\n```\n\n#### Clearing filters\n\n##### Clear all the refinements for all the refined attributes\n\n```js\nhelper.clearRefinements().search();\n```\n\n##### Clear all the refinements for a specific attribute\n\n```js\nhelper.clearRefinements('ANDFacet').search();\n```\n\n##### [ADVANCED] Clear only the exclusions on the \"ANDFacet\" attribute\n\n```js\nhelper\n  .clearRefinements(function (value, attribute, type) {\n    return type === 'exclude' && attribute === 'ANDFacet';\n  })\n  .search();\n```\n\n### Facet utilities\n\n#### Get the values of a facet with the default sort\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetValues('age');\n  // It will be ordered :\n  //  - refined facets first\n  //  - then ordered by number of occurence (bigger count -> higher in the list)\n  //  - then ordered by name (alphabetically)\n});\n```\n\n#### Get the values of a facet with a custom sort\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetValues('age', { sortBy: ['count:asc'] });\n  // It will be ordered by number of occurence (lower number => higher position)\n  // Elements that can be sorted : count, name, isRefined\n  // Type of sort : 'asc' for ascending order, 'desc' for descending order\n});\n```\n\n#### Get the facet stats\n\n_This only apply on numeric based facets/attributes._\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetStats('age');\n});\n```\n\n### Tags\n\nTags are an easy way to do filtering. They are based on a special attribute in the records named `_tags`, which can be a single string value or an array of strings.\n\n#### Add a tag filter for the value \"landscape\"\n\n```js\nhelper.addTag('landscape').search();\n```\n\n#### Remove a tag filter for the value \"landscape\"\n\n```js\nhelper.removeTag('landscape').search();\n```\n\n#### Clear all the tags filters\n\n```js\nhelper.clearTags().search();\n```\n\n### Pagination\n\n#### Get the current page\n\n```js\nhelper.getPage();\n```\n\n#### Change page\n\n```js\nhelper.setPage(3).search();\n```\n\n#### Automatic reset to page 0\n\nDuring a search, changing the parameters will update the result set, which can then change the number of pages in the result set. Therefore, the behavior has been standardized so that any operation that may change the number of page will reset the pagination to page 0.\n\nThis may lead to some unexpected behavior. For example:\n\n```js\nhelper.setPage(4);\nhelper.getPage(); // 4\nhelper.setQuery('foo');\nhelper.getPage(); // 0\n```\n\nNon exhaustive list of operations that trigger a reset:\n\n- refinements (conjunctive, exclude, disjunctive, hierarchical, numeric)\n- tags\n- index (setIndex)\n- setQuery\n- setHitsPerPage\n- setTypoTolerance\n\n### Index\n\nIndex can be changed. The common use case is when you have several slaves with different sort order (sort by relevance, price or any other attribute).\n\n#### Change the current index\n\n```js\nhelper.setIndex('index_orderByPrice').search();\n```\n\n#### Get the current index\n\n```js\nvar currentIndex = helper.getIndex();\n```\n\n### One time query\n\nSometime it's convenient to reuse the current search parameters with small changes without changing the state stored in the helper. That's why there is a function called `searchOnce`. This method does not trigger `change` or `error` events.\n\nIn the following, we are using `searchOnce` to fetch only a single element using all the other parameters already set in the search parameters.\n\n#### Using searchOnce with a callback\n\n```js\nvar state = helper.searchOnce(\n  { hitsPerPage: 1 },\n  function (error, content, state) {\n    // if an error occured it will be passed in error, otherwise its value is null\n    // content contains the results formatted as a SearchResults\n    // state is the instance of SearchParameters used for this search\n  }\n);\n```\n\n#### Using searchOnce with a promise\n\n```js\nvar state1 = helper.searchOnce({ hitsPerPage: 1 }).then(function (res) {\n  // res contains\n  // {\n  //   content : SearchResults\n  //   state : SearchParameters (the one used for this specific search)\n  // }\n});\n```\n\n### Query parameters\n\nThere are lots of other parameters you can set.\n\n#### Set a parameter at the initialization of the helper\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hitsPerPage: 50,\n});\n```\n\n#### Set a parameter later\n\n```js\nhelper.setQueryParameter('hitsPerPage', 20).search();\n```\n\n#### List of parameters that can be set\n\n<table cellspacing=\"0\" cellpadding=\"0\" class=\"params\">\n  <tbody>\n    <tr>\n      <td valign=\"top\" class=\"td1\">\n        <p class=\"p1\"><span class=\"s1\"><b>Name</b></span></p>\n      </td>\n      <td valign=\"top\" class=\"td2\">\n        <p class=\"p1\"><span class=\"s1\"><b>Type</b></span></p>\n      </td>\n      <td valign=\"top\" class=\"td3\">\n        <p class=\"p1\"><span class=\"s1\"><b>Description</b></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">advancedSyntax</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the advanced syntax.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/advancedSyntax/\">advancedSyntax on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">allowTyposOnNumericTokens</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the engine allow typos on numerics.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/allowTyposOnNumericTokens/\">allowTyposOnNumericTokens on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">analytics</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the analytics</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/analytics/\">analytics on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">analyticsTags</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Tag of the query in the analytics.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/analyticsTags/\">analyticsTags on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundLatLng</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Center of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLng/\">aroundLatLng on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundLatLngViaIP</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Center of the search, retrieve from the user IP.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLngViaIP/\">aroundLatLngViaIP on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundPrecision</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Precision of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundPrecision/\">aroundPrecision on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundRadius</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Radius of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundRadius/\">aroundRadius on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minimumAroundRadius</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Minimum radius of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minimumAroundRadius/\">minimumAroundRadius on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToHighlight</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to highlight</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToHighlight/\">attributesToHighlight on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToRetrieve</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to retrieve</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToRetrieve/\">attributesToRetrieve on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToSnippet</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to snippet</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToSnippet/\">attributesToSnippet on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">disjunctiveFacets</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">Array.&lt;string&gt;</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">All the declared disjunctive facets</span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">distinct</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean|number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Remove duplicates based on the index setting attributeForDistinct</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/distinct/\">distinct on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">facets</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">Array.&lt;string&gt;</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">All the facets that will be requested to the server</span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">filters</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">Add filters to the query (similar to WHERE clauses)</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/filters/\">filters on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">getRankingInfo</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">integer</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the ranking informations in the response</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/getRankingInfo/\">getRankingInfo on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">hitsPerPage</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of hits to be returned by the search API</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/hitsPerPage/\">hitsPerPage on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">ignorePlurals</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the plurals be ignored</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/ignorePlurals/\">ignorePlurals on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">insideBoundingBox</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Geo search inside a box.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/insideBoundingBox/\">insideBoundingBox on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">insidePolygon</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Geo search inside a polygon.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/insidePolygon/\">insidePolygon on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">maxValuesPerFacet</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of values for each facetted attribute</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/maxValuesPerFacet/\">maxValuesPerFacet on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minWordSizefor1Typo</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of characters to wait before doing one character replacement.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minWordSizefor1Typo/\">minWordSizefor1Typo on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minWordSizefor2Typos</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of characters to wait before doing a second character replacement.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minWordSizefor2Typos/\">minWordSizefor2Typos on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">optionalWords</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Add some optional words to those defined in the dashboard</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/optionalWords/\">optionalWords on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">page</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">The current page number</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"algolia.com/doc/api-reference/api-parameters/page/\">page on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">query</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Query string of the instant search. The empty string is a valid query.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/query/\">query on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">queryType</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">How the query should be treated by the search engine. Possible values: prefixAll, prefixLast, prefixNone</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/queryType/\">queryType on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">removeWordsIfNoResults</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Possible values are \"lastWords\" \"firstWords\" \"allOptional\" \"none\" (default)</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/removeWordsIfNoResults/\">removeWordsIfNoResults on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">replaceSynonymsInHighlight</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the engine replace the synonyms in the highlighted results.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/replaceSynonymsInHighlight/\">replaceSynonymsInHighlight on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">restrictSearchableAttributes</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Restrict which attribute is searched.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/restrictSearchableAttributes/\">restrictSearchableAttributes on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">synonyms</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the synonyms</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/synonyms/\">synonyms on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">tagFilters</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Contains the tag filters in the raw format of the Algolia API. Setting this parameter is not compatible with the of the add/remove/toggle methods of the tag api.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/tagFilters/\">tagFilters on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">typoTolerance</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">How the typo tolerance behave in the search engine. Possible values: true, false, min, strict</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/typoTolerance/\">typoTolerance on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n  </tbody>\n</table>\n\n### Results format\n\nHere is an example of a result object you get with the `result` event.\n\n```js\n{\n   \"hitsPerPage\": 10,\n   \"processingTimeMS\": 2,\n   \"facets\": [\n      {\n         \"name\": \"type\",\n         \"data\": {\n            \"HardGood\": 6627,\n            \"BlackTie\": 550,\n            \"Music\": 665,\n            \"Software\": 131,\n            \"Game\": 456,\n            \"Movie\": 1571\n         },\n         \"exhaustive\": false\n      },\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"Free shipping\": 5507\n         },\n         \"name\": \"shipping\"\n      }\n   ],\n   \"hits\": [\n      {\n         \"thumbnailImage\": \"http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_54x108_s.gif\",\n         \"_highlightResult\": {\n            \"shortDescription\": {\n               \"matchLevel\": \"none\",\n               \"value\": \"Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection\",\n               \"matchedWords\": []\n            },\n            \"category\": {\n               \"matchLevel\": \"none\",\n               \"value\": \"Computer Security Software\",\n               \"matchedWords\": []\n            },\n            \"manufacturer\": {\n               \"matchedWords\": [],\n               \"value\": \"Webroot\",\n               \"matchLevel\": \"none\"\n            },\n            \"name\": {\n               \"value\": \"Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows\",\n               \"matchedWords\": [],\n               \"matchLevel\": \"none\"\n            }\n         },\n         \"image\": \"http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_105x210_sc.jpg\",\n         \"shipping\": \"Free shipping\",\n         \"bestSellingRank\": 4,\n         \"shortDescription\": \"Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection\",\n         \"url\": \"http://www.bestbuy.com/site/webroot-secureanywhere-internet-security-3-devi…d=1219060687969&skuId=1688832&cmp=RMX&ky=2d3GfEmNIzjA0vkzveHdZEBgpPCyMnLTJ\",\n         \"name\": \"Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows\",\n         \"category\": \"Computer Security Software\",\n         \"salePrice_range\": \"1 - 50\",\n         \"objectID\": \"1688832\",\n         \"type\": \"Software\",\n         \"customerReviewCount\": 5980,\n         \"salePrice\": 49.99,\n         \"manufacturer\": \"Webroot\"\n      },\n      ....\n   ],\n   \"nbHits\": 10000,\n   \"disjunctiveFacets\": [\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"5\": 183,\n            \"12\": 112,\n            \"7\": 149,\n            ...\n         },\n         \"name\": \"customerReviewCount\",\n         \"stats\": {\n            \"max\": 7461,\n            \"avg\": 157.939,\n            \"min\": 1\n         }\n      },\n      {\n         \"data\": {\n            \"Printer Ink\": 142,\n            \"Wireless Speakers\": 60,\n            \"Point & Shoot Cameras\": 48,\n            ...\n         },\n         \"name\": \"category\",\n         \"exhaustive\": false\n      },\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"> 5000\": 2,\n            \"1 - 50\": 6524,\n            \"501 - 2000\": 566,\n            \"201 - 500\": 1501,\n            \"101 - 200\": 1360,\n            \"2001 - 5000\": 47\n         },\n         \"name\": \"salePrice_range\"\n      },\n      {\n         \"data\": {\n            \"Dynex™\": 202,\n            \"Insignia™\": 230,\n            \"PNY\": 72,\n            ...\n         },\n         \"name\": \"manufacturer\",\n         \"exhaustive\": false\n      }\n   ],\n   \"query\": \"\",\n   \"nbPages\": 100,\n   \"page\": 0,\n   \"index\": \"bestbuy\"\n}\n```\n\n## Browser support\n\nThis project works on any [ES5](https://en.wikipedia.org/wiki/ECMAScript#5th_Edition) browser, basically >= IE9+.\n","packages/algoliasearch-helper/examples/readme.md":"The examples moved to [codepen](http://codepen.io/collection/nwWJbe/).\n","packages/create-instantsearch-app/README.md":"# Create InstantSearch App\n\n> ⚡️ Build InstantSearch apps at the speed of thought.\n\n[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Build Status][ci-svg]][ci-url]\n\n`create-instantsearch-app` is a command line utility that helps you quick start your InstantSearch app using any [Algolia][algolia-website] InstantSearch flavor ([InstantSearch.js][instantsearchjs-github], [React InstantSearch][react-instantsearch-github], [Vue InstantSearch][vue-instantsearch-github], [InstantSearch iOS][instantsearch-ios-github] and [InstantSearch Android][instantsearch-android-github]).\n\n<p align=\"center\">\n  <img src=\"preview.png\" width=\"800\" alt=\"Preview\">\n</p>\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Get started](#get-started)\n- [Usage](#usage)\n- [API](#api)\n- [Tutorials](#tutorials)\n- [Previews](#previews)\n- [Contributing](#contributing)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## Get started\n\n> The tool requires Node ≥ 8.\n\n```\nnpx create-instantsearch-app my-app\ncd my-app\nnpm start\n```\n\n> [`npx`](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) is a tool introduced in `npm@5.2.0` that makes it possible to run CLI tools hosted on the npm registry.\n\n---\n\nAlternatively, you can use [Yarn](https://yarnpkg.com):\n\n```\nyarn create instantsearch-app my-app\ncd my-app\nyarn start\n```\n\n> **Important** Selecting 'Y' when being prompted to ‘Enable user events’ activates the [`insights`](https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/#widget-param-insights) option for compatible templates, allowing Algolia to process your user Events. Events can unlock powerful features, enhancing your application's effectiveness and we encourage you to consider enabling this valuable functionality. Please review our [API reference](https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/#widget-param-insights) for more details about Events collection and settings.\n\n## Usage\n\nThis package comes with the module `createInstantSearchApp(path, options?)` and the command-line tool `create-instantsearch-app`.\n\n```\n$ create-instantsearch-app --help\n\n  Usage: create-instantsearch-app <project-directory> [options]\n\n  Options:\n\n    -v, --version                                      output the version number\n    --name <name>                                      The name of the application\n    --app-id <appId>                                   The application ID\n    --api-key <apiKey>                                 The Algolia search API key\n    --index-name <indexName>                           The main index of your search\n    --attributes-to-display <attributesToDisplay>      The attributes of your index to display\n    --image-attribute <imageAttribute>                 The attribute of your index to use for image display\n    --attributes-for-faceting <attributesForFaceting>  The attributes for faceting\n    --template <template>                              The InstantSearch template to use\n    --library-version <libraryVersion>                 The version of the library\n    --config <config>                                  The configuration file to get the options from\n    --no-installation                                  Ignore dependency installation\n    --no-interactive                                   Do not ask any interactive questions\n    -h, --help                                         output usage information\n```\n\n#### `--template`\n\nSupported templates are:\n\n- [`InstantSearch.js`][instantsearchjs-github]\n- [`React InstantSearch`][react-instantsearch-github]\n- [`React InstantSearch Native`][react-instantsearch-github]\n- [`Vue InstantSearch`][vue-instantsearch-github]\n- [`InstantSearch iOS`][instantsearch-ios-github]\n- [`InstantSearch Android`][instantsearch-android-github]\n\n```\ncreate-instantsearch-app my-app --template \"React InstantSearch\"\n```\n\nYou can also [create your own template](docs/custom-templates.md) and specify its path.\n\n#### `--config`\n\nThe `config` flag is handy to automate app generations.\n\n<h6 align=\"center\">config.json</h6>\n\n```json\n{\n  \"name\": \"my-app\",\n  \"template\": \"InstantSearch.js\",\n  \"libraryVersion\": \"2.8.0\",\n  \"appId\": \"MY_APP_ID\",\n  \"apiKey\": \"MY_API_KEY\",\n  \"indexName\": \"MY_INDEX_NAME\",\n  \"searchPlaceholder\": \"Search\",\n  \"attributesToDisplay\": [\"name\", \"description\"],\n  \"imageAttribute\": \"image\",\n  \"attributesForFaceting\": [\"brand\", \"location\"],\n  \"enableInsights\": true\n}\n```\n\nCreate the app based on this configuration:\n\n```\ncreate-instantsearch-app my-app --config config.json\n```\n\n## API\n\n`create-instantsearch-app` is based on the module `createInstantSearchApp(path, options?)`. The same [camel cased](https://en.wikipedia.org/wiki/Camel_case) options as the CLI are available.\n\n```javascript\nconst createInstantSearchApp = require('create-instantsearch-app');\n\nconst app = createInstantSearchApp('~/lab/my-app', {\n  template: 'InstantSearch.js',\n  libraryVersion: '2.0.0',\n  attributesToDisplay: ['name', 'description'],\n  imageAttribute: 'image',\n  attributesForFaceting: ['keywords'],\n  enableInsights: true,\n});\n\napp.create().then(() => console.log('App generated!'));\n```\n\n## Tutorials\n\n- [Creating a custom template](docs/custom-templates.md)\n- [Deploying an InstantSearch App](docs/deploy.md)\n\n## Previews\n\nYou can use the web templates on CodeSandbox:\n\n- [InstantSearch.js](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/instantsearch.js)\n- [React InstantSearch](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/react-instantsearch)\n- [Vue InstantSearch](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/vue-instantsearch)\n- [Autocomplete.js](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/autocomplete.js)\n- [JavaScript Client](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/javascript-client)\n- [JavaScript Helper](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/javascript-helper)\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to the code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nCreate InstantSearch App is [MIT licensed](LICENSE).\n\n<!-- Badges -->\n\n[version-svg]: https://img.shields.io/npm/v/create-instantsearch-app.svg?style=flat-square\n[package-url]: https://npmjs.org/package/create-instantsearch-app\n[ci-svg]: https://img.shields.io/circleci/project/github/algolia/create-instantsearch-app.svg?style=flat-square\n[ci-url]: https://circleci.com/gh/algolia/instantsearch\n[license-image]: https://img.shields.io/badge/license-MIT-green.svg?style=flat-square\n[license-url]: LICENSE\n\n<!-- Links -->\n\n[algolia-website]: https://www.algolia.com/?utm_medium=social-owned&utm_source=GitHub&utm_campaign=create-instantsearch-app%20repository\n[instantsearchjs-github]: https://github.com/algolia/instantsearch/tree/master/packages/instantsearch.js\n[react-instantsearch-github]: https://github.com/algolia/instantsearch/tree/master/packages/react-instantsearch\n[vue-instantsearch-github]: https://github.com/algolia/instantsearch/tree/master/packages/vue-instantsearch\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20Create+InstantSearch+App\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20Create+InstantSearch+App&title=Feature%20request%3A%20\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n","packages/create-instantsearch-app/docs/custom-templates.md":"# Creating a custom template\n\nBesides all the InstantSearch templates provided, the Create InstantSearch API enables you to create your own template.\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Defining the template](#defining-the-template)\n  - [`libraryName`](#libraryname)\n  - [`tasks`](#tasks)\n- [Injecting values to the template](#injecting-values-to-the-template)\n- [Using the template](#using-the-template)\n- [Examples](#examples)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## Defining the template\n\nOnce you've created a project directory, you can turn it into a template by adding a file `.template.js` at its root. This is the configuration file that drives `createInstantSearchApp()` to bootstrap your application.\n\nThis definition file returns a object with several properties.\n\n<h6 align=\"center\">.template.js</h6>\n\n```javascript\nconst { execSync } = require('child_process');\n\nmodule.exports = {\n  libraryName: 'react-instantsearch',\n  tasks: {\n    install(config) {\n      execSync(`cd ${config.path} && npm install`);\n    },\n    teardown(config) {\n      console.log('Begin by running: npm start');\n    },\n  },\n};\n```\n\n### `libraryName`\n\n> `string` (optional) | no default\n\nThe name of the [npm](https://www.npmjs.com) library to fetch versions for.\n\n### `tasks`\n\n> `object` | defaults as follows\n\nThe tasks are hooks that define your [application generation lifecycle](#lifecycle). Each of these hooks are functions which receives the [app configuration](#configuration-object) as parameters. You can use this configuration to drive the application generation.\n\n#### `setup(config: object)`\n\n> No default\n\nCan be leveraged to check the project requirements.\n\n#### `build(config: object)`\n\n> Defaults to copying and injecting the values\n\nCan be leveraged to generate the project differently.\n\n#### `install(config: object)`\n\n> No default\n\nCan be leveraged to install dependencies with a package manager.\n\n#### `clean(config: object)`\n\n> Defaults to deleting the project which installation has failed\n\nCan be leveraged to warn the user that the installation has failed.\n\n#### `teardown(config: object)`\n\n> No default\n\nCan be leveraged to display instructions to run the app.\n\n<h6 align=\"center\" id=\"lifecycle\">Application generation lifeycle</h6>\n\n![Setup → Build → Install → Clean (if installation fails) → Teardown](https://user-images.githubusercontent.com/6137112/41421858-f838c2a6-6ff7-11e8-8cef-4cc07f1f4f44.png)\n\n<h6 align=\"center\" id=\"configuration-object\"><code>config</code> object</h6>\n\n```js\n{\n  name: 'app-name',\n  path: '/dev/app-name',\n  template: 'InstantSearch.js',\n  libraryVersion: '2.9.0',\n  appId: 'APP_ID',\n  apiKey: 'API_KEY',\n  indexName: 'INDEX_NAME',\n  attributesToDisplay: ['title', 'description'],\n  installation: true,\n  silent: false,\n}\n```\n\n## Injecting values to the template\n\nBy default, the `build` task supports the [Handlebars](https://handlebarsjs.com) syntax to inject values passed to Create InstantSearch App. You can explicitly suffix the template files with `.hbs` or leave them as is (`.js` in this case).\n\n<h6 align=\"center\">App.js.hbs</h6>\n\n```handlebars\nimport React, { Component } from 'react';\nimport algoliasearch from 'algoliasearch/lite';\nimport {\n  InstantSearch,\n  Hits,\n  SearchBox,\n  {{#if attributesToDisplay}}\n  Highlight,\n  {{/if}}\n} from 'react-instantsearch-dom';\n\nconst searchClient = algoliasearch(\n  '{{appId}}',\n  '{{apiKey}}'\n);\n\n<InstantSearch searchClient={searchClient} indexName=\"{{indexName}}\">\n  <SearchBox placeholder=\"{{searchPlaceholder}}\" />\n  {/* ... */}\n</InstantSearch>\n```\n\n## Using the template\n\nCreate InstantSearch App reads your `.template.js` file and gets the specified tasks in the provided template.\n\nUsing the API:\n\n```javascript\nconst app = createInstantSearchApp('my-app', {\n  template: '/path/to/my-custom-template',\n});\n\napp.create();\n```\n\nUsing the CLI:\n\n```\ncreate-instantsearch-app my-app --template /path/to/my-custom-template\n```\n\n## Examples\n\nYou can find all the officially supported templates in the [`src/templates/`](../src/templates) folder.\n","packages/create-instantsearch-app/docs/deploy.md":"# Deploy an InstantSearch app\n\nCreate InstantSearch App generates applications that are ready to be deployed. They rely on bundlers and framework toolkits (Parcel, Create React App, Vue CLI, etc.).\n\nYour app can be deployed in a single command, assuming you're located in its folder.\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [CodeSandbox](#codesandbox)\n- [Surge](#surge)\n- [Vercel](#vercel)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## CodeSandbox\n\n```\nnpx codesandbox .\n```\n\n[Learn more about CodeSandbox →](https://codesandbox.io)\n\n## Surge\n\n```\nnpx surge\n```\n\n[Learn more about Surge →](https://surge.sh)\n\n## Vercel\n\n```\nnpx vercel\n```\n\n[Learn more about Vercel →](https://vercel.com)\n","packages/create-instantsearch-app/src/templates/Autocomplete/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/create-instantsearch-app/src/templates/InstantSearch.js widget/README.md":"# {{ name }}\n\n{{ description }}\n\n---\n\n[![MIT](https://img.shields.io/npm/l/{{ packageName }})](./LICENSE) [![NPM version](http://img.shields.io/npm/v/{{ packageName }}.svg)](https://npmjs.org/package/{{ packageName }})\n\n## Install\n\n```bash\nnpm install {{ packageName }}\n# or\nyarn add {{ packageName }}\n```\n\n## Widget\n\n### Usage\n\n```js\nimport instantsearch from 'instantsearch.js';\nimport algoliasearch from 'algoliasearch/lite';\nimport { {{ camelCaseName }} } from '{{ packageName }}';\n\nconst searchClient = algoliasearch('appId', 'apiKey');\n\nconst search = instantsearch({\n  indexName: 'indexName',\n  searchClient,\n});\n\nsearch.addWidgets([\n  {{ camelCaseName }}({\n    // widget parameters\n  }),\n]);\n\nsearch.start();\n```\n\n### Options\n\n| Option | Type | Required | Default | Description |\n| :-- | :-- | :-- | :-- | --- |\n| [`container`](#container) | `string` or `HTMLElement` | true | - | The element to insert the widget into. |\n| [`option1`](#option1) | `...` | true | - | REPLACE WITH THE DESCRIPTION FOR THIS OPTION |\n\n#### container\n\n> `string | Element` | **required**\n\nThe element to insert the widget into.\n\nThis can be either a valid CSS Selector:\n\n```js\n{{ camelCaseName }}({\n  container: '#{{ name }}',\n  // ...\n});\n```\n\nor an `HTMLElement`:\n\n```js\n{{ camelCaseName }}({\n  container: document.querySelector('#{{ name }}'),\n  // ...\n});\n```\n\n#### option1\n\n> `...` | **required**\n\nREPLACE WITH THE DESCRIPTION FOR THIS OPTION\n\n```js\n{{ camelCaseName }}({\n  option1: 'value',\n  // ...\n});\n```\n\n## Connector\n\n### Usage\n\n```js\nimport { connect{{ pascalCaseName }} } from '{{ packageName }}';\n\n// 1. Create a render function\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  // Rendering logic\n};\n\n// 2. Create the custom widget\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }}\n);\n\n// 3. Instantiate\nsearch.addWidgets([\n  custom{{ pascalCaseName }}({\n    // instance params\n  }),\n]);\n```\n\n### Options\n\n#### option1\n\n> `...`\n\nREPLACE WITH THE DESCRIPTION FOR THIS RENDERING ITEM\n\n```js\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  // show how to use this render option\n};\n\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }},\n);\n\nsearch.addWidgets([\n  custom{{pascalCaseName }}({\n    // ...\n  }),\n]);\n```\n\n#### widgetParams\n\n> `object`\n\nAll original widget options forwarded to the render function.\n\n```js\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  const { widgetParams } = renderOptions;\n  widgetParams.container.innerHTML = '...';\n};\n\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }},\n);\n\nsearch.addWidgets([\n  custom{{ pascalCaseName }}({\n    container: document.querySelector('#{{ name }}'),\n    // ...\n  }),\n]);\n```\n\n## Contributing\n\nTo start contributing to code, you need to:\n\n1. [Fork the project](https://docs.github.com/en/get-started/quickstart/fork-a-repo)\n2. [Clone the repository](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository)\n3. Install the dependencies: `yarn`\n4. Run the development mode: `yarn start`\n\nPlease read [our contribution process](./CONTRIBUTING.md) to learn more.\n\n---\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n","packages/create-instantsearch-app/src/templates/InstantSearch.js/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/create-instantsearch-app/src/templates/JavaScript Client/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/create-instantsearch-app/src/templates/JavaScript Helper/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/create-instantsearch-app/src/templates/React InstantSearch Native/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n","packages/create-instantsearch-app/src/templates/React InstantSearch/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n","packages/create-instantsearch-app/src/templates/Vue InstantSearch with Vue 3/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/create-instantsearch-app/src/templates/Vue InstantSearch/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/instantsearch-codemods/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Codemods](#codemods)\n  - [`rish-to-ris`](#rish-to-ris)\n  - [`ris-v6-to-v7`](#ris-v6-to-v7)\n  - [`addWidget-to-addWidgets`](#addwidget-to-addwidgets)\n  - [Notes](#notes)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Codemods\n\nThese codemods (code transformers) can be ran with [jscodeshift](https://github.com/facebook/jscodeshift).\n\n### `rish-to-ris`\n\nThis will replace all imports from `react-instantsearch-hooks` to their `react-instantsearch` equivalent.\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#rish-to-ris' <path>\n```\n\n### `ris-v6-to-v7`\n\nThis will:\n\n- Replace `react-instantsearch-dom` imports to `react-instantsearch`\n- Replace prop names to their new equivalent\n- Replace `translations` keys to their new equivalent if they are defined inline, otherwise it will add a TODO comment\n- Add some TODO comments to help you migrate\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#ris-v6-to-v7' <path>\n```\n\n### `addWidget-to-addWidgets`\n\nThis will replace all `addWidget` calls to `addWidgets` and `removeWidget` calls to `removeWidgets`.\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#addWidget-to-addWidgets' <path>\n```\n\n### Notes\n\nIf you are using a formatter or a linter, make sure to run its autofixing after this transformation, since code can be formatted differently after it has been transformed. In our repository, that would be:\n\n```\nyarn format\n```\n","packages/instantsearch-ui-components/CLAUDE.md":"# instantsearch-ui-components\n\nThe **framework-agnostic UI layer** — the shared markup/layout for widgets, reused by every flavor. This is where a widget's DOM structure and `ais-*` CSS classes live for **newer** widgets. (Older widgets still define their layout per-flavor; see \"Old vs new\" below.)\n\n## The factory + renderer pattern\n\nComponents are **not** React/Preact/Vue components directly. Each is a factory that takes a renderer and returns a component:\n\n```tsx\n/** @jsx createElement */\nimport { cx } from '../lib';\nimport type { Renderer, ComponentProps } from '../types';\n\nexport function createHitsComponent({ createElement, Fragment }: Renderer) {\n  return function Hits(props) {\n    return <div className={cx('ais-Hits', props.classNames.root)}>…</div>;\n  };\n}\n```\n\n- **`Renderer` = `{ createElement, Fragment }`** is _injected_ by the consumer (`src/types/Renderer.ts`). Defaults are Preact's, but React passes `react`'s `createElement` and Vue passes its own `h` (wired up via the `renderCompat` helper in `vue-instantsearch`). **Never import `preact`/`react`/`vue` directly** — go through the injected renderer. The `/** @jsx createElement */` pragma at the top of each file is what wires JSX to the injected function.\n- Class names go through **`cx`** (`src/lib/cx.ts`) and follow the `ais-<Widget>-<element>` contract that `instantsearch.css` themes — treat those class names as a public API.\n- Props are typed with `ComponentProps<'div'>`-style helpers from `src/types`; expose a `classNames` partial so consumers can extend styling.\n\n## Layout\n\n- `src/components/<Name>.tsx` — a `create<Name>Component` factory. Exported from `src/components/index.ts` → `src/index.ts`.\n- Grouped families: `src/components/autocomplete/`, `src/components/chat/`, `src/components/recommend-shared/` (+ the recommend widgets `RelatedProducts`, `FrequentlyBoughtTogether`, `LookingSimilar`, `TrendingItems`, `TrendingFacets`).\n- `src/lib/` — `cx`, `stickToBottom`, shared `utils`. `src/types/` — `Renderer`, `ComponentProps`, `Hooks`, `Recommend`, `shared`.\n- Runtime deps are minimal on purpose (`@swc/helpers`); **no framework as a dependency**. Markdown rendering uses a vendored copy of `markdown-to-jsx` in `src/lib/markdown-to-jsx/` (React removed, see its README); it is linted, formatted and type-checked like the rest.\n\n## Old vs new (important)\n\n- **New widgets:** layout lives here and is consumed by all flavors — React via `import { create<Name>Component } from 'instantsearch-ui-components'`, Vue via the `renderCompat` helper, JS via the Preact renderer.\n- **Older widgets:** layout was defined per-flavor — JS in `packages/instantsearch.js/src/components/` (Preact), React in `packages/react-instantsearch/src/ui/`. The migration direction is to consolidate that markup here; some JS-package components are now thin wrappers that import from this package.\n- When adding/changing **shared** layout, do it here. When touching a widget that still defines its own markup, check whether it should be migrated rather than forked.\n\n## Working here\n\n```bash\nyarn workspace instantsearch-ui-components build       # tsc/rollup + build:types\nyarn jest packages/instantsearch-ui-components/src/components/__tests__/<Name>   # from repo root\n```\n\n- Tests are co-located in `src/components/__tests__/`. Test the component by passing a concrete renderer (e.g. Preact's) — assert rendered structure and class names, not implementation.\n- A markup or class-name change here **ripples to React, Vue, and JS at once** — verify consumers and keep the `ais-*` contract stable (a class rename is a breaking change that also touches `instantsearch.css`).\n","packages/instantsearch-ui-components/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [instantsearch-ui-components](#instantsearch-ui-components)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# instantsearch-ui-components\n\nInstantSearch UI Components is an open-source UI library used by InstantSearch to build UI for search and discovery interfaces. It exports UI components that are compatible with InstantSearch.js, React InstantSearch, and Vue InstantSearch.\n\n> Note: `instantsearch-ui-components` exists for internal usage and isn't designed for public consumption.\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nInstantSearch UI Components is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","packages/instantsearch-ui-components/src/lib/markdown-to-jsx/README.md":"Vendored copy of [`markdown-to-jsx`](https://github.com/quantizor/markdown-to-jsx) v7.7.15 (MIT, see `LICENSE`), taken from the original TypeScript embedded in the npm package's source map.\n\nChanges from upstream:\n\n- React types are replaced by the shared `Renderer` types, so the module has no `react` import, not even as a type.\n- `createElement` must be passed in options (no `React.createElement` fallback).\n- The `<Markdown>` React component and the default export are removed.\n- Raw HTML parsing is removed: the `htmlBlock` and `htmlSelfClosing` rules, the `disableParsingRawHTML` option and the state, types and helpers that only they used. HTML comments are still dropped.\n- Formatted and linted with the repo's config, and type-checked under its strict TypeScript config: non-null assertions, `any` casts for dynamic indexing, `null` added to a few declared types, unused parameters prefixed with `_`, `var` replaced by `let`/`const`, a sparse array literal written with an explicit `undefined`, `LOOKAHEAD` renamed to `lookahead`, type parameters renamed to the `T` prefix convention, and `eslint-disable` comments for the dev-only `console.warn` calls, the namespace and a deprecated type. The emitted JavaScript is otherwise unchanged.\n","packages/instantsearch.css/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Getting started](#getting-started)\n- [Installation](#installation)\n- [Available themes](#available-themes)\n- [Browser support](#browser-support)\n- [Implemented versions](#implemented-versions)\n- [Contributing](#contributing)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n<p align=\"center\">\n  <p align=\"center\">\n    InstantSearch.css is a CSS library to style InstantSearch-powered search experiences.\n  </p>\n</p>\n\n---\n\n[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Build Status][ci-svg]][ci-url] [![Website][website-svg]][website-url]\n\n## Getting started\n\nUsing InstantSearch.css is as simple as adding this CSS code to your existing InstantSearch app:\n\n```js\n// Include only the reset\nimport 'instantsearch.css/themes/reset.css';\n\n// Or include the full Satellite theme\nimport 'instantsearch.css/themes/satellite.css';\n```\n\nTo learn more about the library, follow the guide on how to style your InstantSearch widgets:\n\n- [InstantSearch.js](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/js/#style-your-widgets)\n- [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/react/#style-your-widgets)\n- [Vue InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/vue/#style-your-widgets)\n\n## Installation\n\n```sh\nnpm install instantsearch.css\n# or\nyarn add instantsearch.css\n```\n\n## Available themes\n\nInstantSearch.css exposes two themes:\n\n- [Algolia](src/themes/algolia.scss)\n- [Satellite](src/themes/satellite.scss)\n\nIf you don't need a theme, you can still use the [reset styles](src/themes/reset.scss) to start from a clean slate.\n\n**The reset styles are included in all the provided themes.** You don't need to import them if you use a theme.\n\n## Browser support\n\nWe support the **last two versions of major browsers** (Chrome, Edge, Firefox, Safari).\n\n## Implemented versions\n\nInstantSearch.css is a living standard. This table tracks down the version implemented in each InstantSearch flavor.\n\n| Project             | Version |\n| ------------------- | ------- |\n| InstantSearch.js    | 7       |\n| React InstantSearch | 7       |\n| Vue InstantSearch   | 7       |\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nInstantSearch.css is [MIT licensed][license-url].\n\n<!-- Badges -->\n\n[version-svg]: https://img.shields.io/npm/v/instantsearch.css.svg?style=flat-square\n[package-url]: https://npmjs.org/package/instantsearch.css\n[ci-svg]: https://img.shields.io/circleci/project/github/algolia/instantsearch.svg?style=flat-square\n[ci-url]: https://circleci.com/gh/algolia/instantsearch\n[license-image]: http://img.shields.io/badge/license-MIT-green.svg?style=flat-square\n[website-svg]: https://img.shields.io/badge/website-instantsearchjs.netlify.app/specs-yellow?style=flat-square\n[website-url]: https://instantsearchjs.netlify.app/specs/\n[license-url]: LICENSE\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20InstantSearch.css\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20InstantSearch.css&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20InstantSearch.css\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20InstantSearch.css%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20InstantSearch.css%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20InstantSearch.css%22\n","packages/instantsearch.js/CLAUDE.md":"# instantsearch.js (core / vanilla JS)\n\nThe core package. **Every connector lives here and is consumed by all flavors** — React hooks and Vue components wrap these. A behavior change usually belongs in a connector here, not in a flavor package.\n\n## Layout\n\n- `src/connectors/<name>/connect<Pascal>.ts` — framework-agnostic logic. Signature: `connect<Widget>(renderFn, unmountFn)` returns a widget factory. Owns state, lifecycle, and search-param mutations. Exported from `src/connectors/index.ts`.\n- `src/widgets/<name>/<name>.tsx` — vanilla JS widget = connector + default template/rendering. Exported from `src/widgets/index.ts`.\n- `src/components/` — the **JS flavor's Preact components** (the widgets' markup). This is the _legacy_ home for widget layout; newer widgets pull their layout from the shared `instantsearch-ui-components` package instead, and some components here are now thin wrappers that import from it. Add **new shared** markup to `instantsearch-ui-components`, not here.\n- `src/lib/` — the InstantSearch runtime: lifecycle orchestration, routing, helper integration.\n- `src/lib/ai-lite/` — hand-written reimplementation of the Vercel AI SDK's UI layer (`UIMessage` parts, the SSE chunk protocol, the `Chat` state machine `connectChat` drives). It replaced `ai@^5.0.18` in #6880 and tracks nothing upstream: AI SDK fixes land on the `ai@6` line and never reach us. **Before changing its semantics, diff the matching file under [`vercel/ai/packages/ai/src/ui`](https://github.com/vercel/ai/tree/main/packages/ai/src/ui)** — the auto-continue predicate silently missed vercel/ai#9944 (don't resend provider-executed tool turns) for months. Deliberate gaps: no tool-approval flow, no metadata/data-part schema validation, no `finishReason`.\n- `src/types/` — public type definitions.\n\n## Working here\n\n```bash\nyarn workspace instantsearch.js build      # rollup → esm + cjs + umd + .d.ts\nyarn jest packages/instantsearch.js/src/connectors/<name>   # run one connector's tests (from repo root)\n```\n\n- Connectors return a typed render state — don't return untyped state (oxlint rule).\n- A new widget that only varies presentation should **reuse an existing connector** (e.g. `menuSelect` reuses `connectMenu`) and set a distinct `$$widgetType`; don't fork the connector.\n- Cross-flavor behavior is covered by `tests/common/connectors/<name>/`; flavor-agnostic logic tests go there, JS-specific ones stay in `__tests__/`.\n","packages/instantsearch.js/README.md":"<p align=\"center\">\n  <a href=\"https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/\">\n    <img alt=\"InstantSearch.js\" src=\"https://github.com/algolia/instantsearch/blob/master/.github/banner.png?raw=true\">\n  </a>\n\n  <p align=\"center\">\n    InstantSearch.js is a JavaScript library for building performant and instant search experiences with <a href=\"https://www.algolia.com/?utm_source=instantsearch.js&utm_campaign=repository\">Algolia</a>.\n  </p>\n</p>\n\n---\n\nInstantSearch.js is a vanilla JavaScript library that lets you create an instant-search result experience using [Algolia][algolia-website]’s search API. It is part of the InstantSearch family:\n\n**InstantSearch.js** | [React InstantSearch][instantsearch-github] | [Vue InstantSearch][instantsearch-github] | [React InstantSearch Native][instantsearch-github] | [InstantSearch Android][instantsearch-android-github] | [InstantSearch iOS][instantsearch-ios-github]\n\n<details>\n  <summary><strong>Table of contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Why](#why)\n- [Getting started](#getting-started)\n- [Installation](#installation)\n  - [TypeScript users](#typescript-users)\n- [Documentation](#documentation)\n- [Demos](#demos)\n- [Playground](#playground)\n- [Browser support](#browser-support)\n- [Troubleshooting](#troubleshooting)\n- [Contributing](#contributing)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## Why\n\nYou should be using InstantSearch if you want to:\n\n- Design search experiences with best practices\n- Customize your components at will\n- Remain independent from external frameworks\n\n## Getting started\n\nUsing InstantSearch.js is as simple as adding this JavaScript code to your page:\n\n```javascript\n// 1. Instantiate the search\nconst search = instantsearch({\n  indexName: 'instant_search',\n  searchClient: algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76'),\n});\n\nsearch.addWidgets([\n  // 2. Create an interactive search box\n  instantsearch.widgets.searchBox({\n    container: '#searchbox',\n    placeholder: 'Search for products',\n  }),\n\n  // 3. Plug the search results into the product container\n  instantsearch.widgets.hits({\n    container: '#products',\n    templates: {\n      item: (hit, { html, components }) => html`\n        <h3>${components.Highlight({ attribute: 'name', hit })}</h3>\n        <p>${components.Snippet({ attribute: 'description', hit })}</p>\n      `,\n    },\n  }),\n\n  // 4. Make the brands refinable\n  instantsearch.widgets.refinementList({\n    container: '#brand',\n    attribute: 'brand',\n  }),\n]);\n\n// 5. Start the search!\nsearch.start();\n```\n\n<p align=\"center\">\n  <a href=\"https://codesandbox.io/s/github/algolia/doc-code-samples/tree/master/InstantSearch.js/getting-started\" title=\"Edit on CodeSandbox\">\n    <img alt=\"Edit on CodeSandbox\" src=\"https://codesandbox.io/static/img/play-codesandbox.svg\">\n  </a>\n</p>\n\nTo learn more about the library, follow the [getting started](https://www.algolia.com/doc/guides/building-search-ui/getting-started/js/) guide or check how to [add it to your own project](https://www.algolia.com/doc/guides/building-search-ui/installation/js/).\n\n## Installation\n\n```sh\nnpm install instantsearch.js algoliasearch\n# or\nyarn add instantsearch.js algoliasearch\n```\n\n### TypeScript users\n\nTo use InstantSearch.js in a TypeScript environment, depending on your [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) version, you need to import different types.\n\n> You still need to import these types even if you don't use InstantSearch.js with [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript).\n\n#### `algoliasearch` v4.x\n\nThis version uses types provided by both `algoliasearch` and `@algolia/client-search`.\n\n```bash\nyarn add algoliasearch@4 @algolia/client-search\n```\n\n#### `algoliasearch` v3.x\n\n```bash\nyarn add @types/algoliasearch@3\n```\n\n> v3.x is deprecated and will soon no longer be supported.\n\n## Documentation\n\nThe documentation is available on the [Algolia website](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/).\n\n## Demos\n\n| E-commerce | Media | Travel |\n| --- | --- | --- |\n| <a href=\"https://instantsearchjs.netlify.app/examples/e-commerce/\"><img src=\"https://www.algolia.com/doc/assets/images/build-search-ui/demos/e-commerce-2c7ed6b6.png\" width=\"250\" alt=\"E-commerce demo preview\"></a> | <a href=\"https://instantsearchjs.netlify.app/examples/media/\"><img src=\"https://www.algolia.com/doc/assets/images/build-search-ui/demos/media-articles-abc153ab.png\" width=\"250\" alt=\"Media demo preview\"></a> | <a href=\"https://instantsearchjs.netlify.app/examples/tourism/\"><img src=\"https://instantsearchjs.netlify.app/examples/tourism/capture.png\" width=\"250\" alt=\"Tourism demo preview\"></a> |\n\nSee more demos [on the website](https://www.algolia.com/doc/guides/building-search-ui/resources/demos/js/).\n\n## Playground\n\nYou can get to know InstantSearch.js on [this playground](https://codesandbox.io/s/github/algolia/create-instantsearch-app/tree/templates/instantsearch.js).\n\nStart by [adding widgets](https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/js/) and tweaking the display. Once you feel familiar with the library, we recommend following the [getting started guide](https://www.algolia.com/doc/guides/building-search-ui/getting-started/js/).\n\n## Browser support\n\nWe support the **last two versions of major browsers** (Chrome, Edge, Firefox, Safari).\n\nPlease refer to the [browser support](https://www.algolia.com/doc/guides/building-search-ui/installation/js/#browser-support) section in the documentation to use InstantSearch.js on other browsers.\n\n## Troubleshooting\n\nEncountering an issue? Before reaching out to support, we recommend heading to our [FAQ](https://www.algolia.com/doc/guides/building-search-ui/troubleshooting/faq/js/) where you will find answers for the most common issues and gotchas with the library.\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nInstantSearch.js is [MIT licensed][license-url].\n\n<!-- Links -->\n\n[license-url]: LICENSE\n[algolia-website]: https://www.algolia.com/?utm_source=instantsearch.js&utm_campaign=repository\n[instantsearch-github]: https://github.com/algolia/instantsearch/\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20InstantSearch.js\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20InstantSearch.js&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20InstantSearch.js\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20InstantSearch.js%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20InstantSearch.js%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20InstantSearch.js%22\n","packages/instantsearch.js/scripts/transforms/README.md":"## Codemods\n\nThese codemods (code transformers) can be ran with [jscodeshift]((https://github.com/facebook/jscodeshift).\n\n### `addWidget-to-addWidgets`\n\nThis will replace calls of `addWidget(widget)` to `addWidgets([widget])`, as well as `removeWidget(widget) to `removeWidgets([widget])`.\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#addWidget-to-addWidgets' <path>\n```\n\n### Notes\n\nIf you are using a formatter or a linter, make sure to run its autofixing after this transformation, since code can be formatted differently after it has been transformed. In our repository, that would be:\n\n```\nyarn format\n```\n","packages/react-instantsearch-core/README.md":"<p align=\"center\">\n  <a href=\"https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/\">\n    <img alt=\"React InstantSearch\" src=\"https://github.com/algolia/instantsearch/blob/master/.github/react-instantsearch-banner.png?raw=true\">\n  </a>\n</p>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch-core](#react-instantsearch-core)\n  - [Installation](#installation)\n  - [Getting started](#getting-started)\n  - [API reference](#api-reference)\n  - [Documentation](#documentation)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch-core\n\nReact InstantSearch Core is an open-source UI library for React that lets you quickly build a search interface in your front-end application.\n\nInstantSearch’s goal is to help you implement awesome search experiences as smoothly as possible by providing a [complete search ecosystem](https://algolia.com/doc/guides/getting-started/how-algolia-works/#the-full-ecosystem). InstantSearch tackles an important part of this vast goal by providing front-end primitives that you can assemble into unique search interfaces.\n\n<p align=\"center\">\n  <a href=\"https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/default-themes\" title=\"Edit on CodeSandbox\">\n    <img alt=\"Edit on CodeSandbox\" src=\"https://codesandbox.io/static/img/play-codesandbox.svg\">\n  </a>\n</p>\n\n> Note: `react-instantsearch-core` exports renderless components and hooks which can be used for both web and React Native. If you are using React in a web project, we recommend using the package `react-instantsearch` instead, as it includes complete components that render to the DOM.\n\n## Installation\n\nReact InstantSearch Core is available on the npm registry. It relies on [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) to communicate with Algolia APIs.\n\n```sh\nyarn add algoliasearch react-instantsearch-core\n# or\nnpm install algoliasearch react-instantsearch-core\n```\n\n## Getting started\n\nReact InstantSearch Core is a headless React library that lets you create an instant search results experience using Algolia’s search API.\n\nCheck out our [**Getting Started guide**](https://algolia.com/doc/guides/building-search-ui/getting-started/react/) to start implementing a full-featured search experience with React InstantSearch Core.\n\n## API reference\n\nCheck out the [**API reference**](https://www.algolia.com/doc/api-reference/widgets/react/).\n\n## Documentation\n\nThe documentation is available on [algolia.com/doc](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/).\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","packages/react-instantsearch-nextjs/CLAUDE.md":"# react-instantsearch-nextjs (Next.js App Router)\n\nSSR integration for the **App Router** (RSC + streaming). Peer-deps `next` + `react-instantsearch` — it **composes** react-instantsearch, never reimplements search. This is the non-trivial Next.js package; the subtle part is server-render + hydration.\n\n## Public surface\n\n- `InstantSearchNext` (`src/InstantSearchNext.tsx`) — the entry component, a drop-in for `<InstantSearch>` inside the App Router. It does SSR **for you**, so you don't hand-wire the plain-react-instantsearch `getServerState` + `InstantSearchSSRProvider` dance. Props: `routing` (boolean | object) and an optional `instance`.\n\n## How the SSR machinery fits together\n\n- `InitializePromise.ts` + `TriggerSearch.ts` — run the search **during the server (RSC) render** so results are available before HTML is sent.\n- `createInsertHTML.tsx` + `htmlEscape.ts` — serialize that initial server state and **inject it into the streamed HTML** for hydration. `htmlEscape` is a **safety boundary** (escapes the injected payload so it can't break out of the script/HTML context) — don't bypass it when touching the insert path.\n- `useInstantSearchRouting.ts`, `useNextHeaders.tsx`, `useDynamicRouteWarning.ts` — routing via `next/navigation` (not `next/router`). Dynamic routes are warned about (`useDynamicRouteWarning`) because they interact poorly with InstantSearch URL sync.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch-nextjs build\nyarn jest packages/react-instantsearch-nextjs/src/__tests__/<name>   # unit tests, from repo root\nyarn workspace react-instantsearch-nextjs test:e2e                   # Playwright (App Router)\n```\n\n- **Hydration is the failure mode.** A change to the initialize/trigger/insert path can produce server/client markup mismatch — verify there's no hydration warning and that the first paint already reflects server results.\n- e2e lives in `__tests__/e2e/` (Playwright). Browser back/forward on dynamic App Router routes needs file-level `slowMo` in headless mode — see `.claude/rules/e2e.md`.\n- Treat SSR/RSC/hydration work here as **reasoning-heavy** — it's the one corner of the React surface that isn't bounded widget wiring.\n","packages/react-instantsearch-nextjs/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch-nextjs](#react-instantsearch-nextjs)\n  - [Installation](#installation)\n  - [Usage](#usage)\n  - [API](#api)\n    - [`<InstantSearchNext>`](#instantsearchnext)\n    - [`routing` prop](#routing-prop)\n  - [Troubleshooting](#troubleshooting)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch-nextjs\n\nThis package provides server-side rendering for [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) that is compatible with [Next.js 13 App Router](https://nextjs.org/docs/app).\n\n## Installation\n\n```sh\nyarn add react-instantsearch-nextjs\n# or with npm\nnpm install react-instantsearch-nextjs\n```\n\n## Usage\n\n> [!NOTE] You can check this documentation on [Algolia's Documentation website](https://www.algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react/#app-router-experimental).\n\nYour search component must be in its own file, and it shouldn't be named `page.js` or `page.tsx`.\n\nTo render the component in the browser and allow users to interact with it, include the \"use client\" directive at the top of your code.\n\n```diff\n+'use client';\nimport { liteClient as algoliasearch } from 'algoliasearch/lite';\nimport {\n  InstantSearch,\n  SearchBox,\n} from 'react-instantsearch';\n\nconst searchClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');\n\nexport function Search() {\n  return (\n    <InstantSearch indexName=\"YourIndexName\" searchClient={searchClient}>\n      <SearchBox />\n      {/* other widgets */}\n    </InstantSearch>\n  );\n}\n```\n\nImport the `<InstantSearchNext>` component from the `react-instantsearch-nextjs` package, and replace the [`<InstantSearch>`](https://www.algolia.com/doc/api-reference/widgets/instantsearch/react/) component with it, without changing the props.\n\n```diff\n'use client';\nimport { liteClient as algoliasearch } from 'algoliasearch/lite';\nimport {\n- InstantSearch,\n  SearchBox,\n} from 'react-instantsearch';\n+import { InstantSearchNext } from 'react-instantsearch-nextjs';\n\nconst searchClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');\n\nexport function Search() {\n  return (\n-   <InstantSearch indexName=\"YourIndexName\" searchClient={searchClient}>\n+   <InstantSearchNext indexName=\"YourIndexName\" searchClient={searchClient}>\n      <SearchBox />\n      {/* other widgets */}\n\n-   </InstantSearch>\n+   </InstantSearchNext>\n  );\n}\n```\n\nTo serve your search page at `/search`, create an `app/search` directory. Inside it, create a `page.js` file (or `page.tsx` if you're using TypeScript).\n\nMake sure to [configure your route segment to be dynamic](https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic) so that Next.js generates a new page for each request.\n\n```jsx\n// app/search/page.js or app/search/page.tsx\nimport { Search } from './Search'; // change this with the path to your <Search> component\n\nexport const dynamic = 'force-dynamic';\n\nexport default function Page() {\n  return <Search />;\n}\n```\n\nYou can now visit `/search` to see your server-side rendered search page.\n\n## API\n\n### `<InstantSearchNext>`\n\nThe `<InstantSearchNext>` component is a drop-in replacement for the `<InstantSearch>` component. It accepts the same props, and it renders the same UI.\n\nYou can check the [InstantSearch API reference](https://www.algolia.com/doc/api-reference/widgets/instantsearch/react/) for more information.\n\n### `routing` prop\n\nAs with the `<InstantSearch>` component, you can pass a `routing` prop to the `<InstantSearchNext>` component to customize the routing behavior. The difference here is that `routing.router` takes [the same options as the `historyRouter`](https://www.algolia.com/doc/api-reference/widgets/history-router/react/).\n\n## Troubleshooting\n\nIf you're experiencing issues, please refer to the [**Need help?**](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/#need-help) section of the docs, or [open a new issue](https://github.com/algolia/instantsearch.js/issues/new?assignees=&labels=triage&template=BUG_REPORT.yml).\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","packages/react-instantsearch-router-nextjs/CLAUDE.md":"# react-instantsearch-router-nextjs (Next.js Pages Router)\n\nA **thin routing adapter** for the **Pages Router** — not a full SSR layer. It wraps InstantSearch's history router so URL sync plays well with `next/router`. The actual SSR is the standard react-instantsearch `getServerState` + `InstantSearchSSRProvider` pattern in your app, **not** here.\n\n## Public surface\n\n- `createInstantSearchRouterNext({ singletonRouter, serverUrl?, ... })` (`src/index.ts`) — returns a `Router` you pass to `<InstantSearch routing={{ router }}>`. Tagged `$$type: 'ais.nextjs'`.\n\n## What it actually does (and why)\n\n- Builds on `instantsearch.js/es/lib/routers/history`; on the server (`typeof window === 'undefined'`) it returns a simpler router reading from `serverUrl`.\n- `push` uses **`shallow: true`** so refinements don't re-run `getServerSideProps`, and strips the locale (`stripLocaleFromUrl`) to avoid Next i18n path errors.\n- Hooks `routeChangeComplete` + `beforePopState` to trigger SSR **only** on real page changes (back/forward to a different page), not on every refinement. `beforePopState`/`beforeStart`/`beforeDispose`/`routerOptions` let consumers compose their own logic.\n- `stripLocaleFromUrl` (`src/utils/`) handles i18n locale prefixes in the URL.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch-router-nextjs build\nyarn workspace react-instantsearch-router-nextjs test:e2e   # Playwright (Pages Router)\n```\n\n- Surface is small — most behavior lives in the shared history router (`instantsearch.js`) and react-instantsearch SSR core. Keep this package to the **Next-specific routing glue**.\n- The tricky cases are i18n/locale paths and back/forward navigation deciding whether to re-SSR. e2e in `__tests__/e2e/` (Playwright); see `.claude/rules/e2e.md`.\n","packages/react-instantsearch-router-nextjs/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch-router-nextjs](#react-instantsearch-router-nextjs)\n  - [Installation](#installation)\n  - [Usage](#usage)\n  - [API](#api)\n  - [Troubleshooting](#troubleshooting)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch-router-nextjs\n\nThis package is a router for [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) that is compatible with [Next.js](https://nextjs.org/) routing.\n\n> :warning: **This function cannot be used in conjunction with [`getStaticProps()`](https://nextjs.org/docs/api-reference/data-fetching/get-static-props). Use `getServerSideProps()` or client-side rendering instead.**\n\n## Installation\n\n```sh\nyarn add react-instantsearch-router-nextjs\n# or with npm\nnpm install react-instantsearch-router-nextjs\n```\n\n## Usage\n\nYou need to pass the Next.js router singleton that you can import from `next/router`.\n\nIf you are doing SSR with the `getServerState` and `InstantSearchSSRProvider` from `react-instantsearch/server`, you need to pass the `url` prop to `createInstantSearchRouterNext`'s `serverUrl` option :\n\n```js\nimport singletonRouter from 'next/router';\nimport { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';\n\nexport default function Page({ serverState, url }) {\n  return (\n    <InstantSearchSSRProvider {...serverState}>\n      <InstantSearch\n        searchClient={searchClient}\n        indexName=\"instant_search\"\n        routing={{\n          router: createInstantSearchRouterNext({\n            singletonRouter,\n            serverUrl: url,\n          }),\n        }}\n      >\n        {/* ... */}\n      </InstantSearch>\n    </InstantSearchSSRProvider>\n  );\n}\n```\n\nIf you are not doing SSR but only CSR, you can omit the `serverUrl` option:\n\n```js\nimport singletonRouter from 'next/router';\nimport { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';\n\nexport default function Page() {\n  return (\n    <InstantSearch\n      searchClient={searchClient}\n      indexName=\"instant_search\"\n      routing={{ router: createInstantSearchRouterNext({ singletonRouter }) }}\n    >\n      {/* ... */}\n    </InstantSearch>\n  );\n}\n```\n\nLastly, if you had custom routing logic in your app, you can pass it to the `createInstantSearchRouterNext`'s `routerOptions` option:\n\n```js\nimport singletonRouter from 'next/router';\nimport { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';\n\nexport default function Page({ serverState, url }) {\n  return (\n    {/* ... */}\n      <InstantSearch\n        searchClient={searchClient}\n        indexName=\"instant_search\"\n        routing={{\n          router: createInstantSearchRouterNext({\n            singletonRouter,\n            serverUrl: url,\n            routerOptions: {\n              createURL: /* ... */,\n              parseURL: /* ... */,\n            },\n          }),\n           // if you are using a custom `stateMapping` you can still pass it :\n          stateMapping: /* ... */,\n        }}\n      >\n        {/* ... */}\n      </InstantSearch>\n    {/* ... */}\n  );\n}\n```\n\n## API\n\nThe options are :\n\n- `singletonRouter: SingletonRouter`: the required Next.js router singleton.\n- `serverUrl?: string`: the URL of the page on the server. Required if you are doing SSR with `getServerState` and `InstantSearchSSRProvider`.\n- `routerOptions?: RouterOptions`: the options passed to the `history` router. See [the documentation](https://www.algolia.com/doc/api-reference/widgets/history-router/js/) for more details.\n\nFor troubleshooting purposes, some other options are available :\n\n- `beforeStart?: (onUpdate: () => void) => void`: a function called before the router starts. You can use it to inform InstantSearch to update on router events by calling `onUpdate`.\n- `beforeDispose?: () => void`: a function called before the router disposes. You can use it to clean up handlers you added in `beforeStart`.\n- `beforePopState?: (options: { state: NextHistoryState, ownBeforePopState: BeforePopStateCallback, libraryBeforePopState: BeforePopStateCallback }) => boolean`: a function used by the Next.js router to know whether it should trigger SSR when using back/forward buttons. You can use it to override the default one by writing your own logic. The `ownBeforePopState` is the pre-existing handler that you may have set yourself, and the `libraryBeforePopState` is the default one from the library.\n\n## Troubleshooting\n\nIf you're experiencing issues, please refer to the [**Need help?**](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/#need-help) section of the docs, or [open a new issue](https://github.com/algolia/instantsearch/issues/new?assignees=&labels=triage&template=BUG_REPORT.yml).\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","packages/react-instantsearch/CLAUDE.md":"# react-instantsearch\n\nReact DOM bindings. This package = **hooks (from `react-instantsearch-core`) + UI markup (from `instantsearch-ui-components`)**. The search logic itself is the connector in `instantsearch.js`; don't reimplement it here.\n\n## Layer split (where each thing goes)\n\n- `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts` — the **hook** that wraps the `instantsearch.js` connector. Headless, no DOM. Exported from that package's `src/index.ts`.\n- `packages/react-instantsearch/src/widgets/<Pascal>.tsx` — the **component**: calls the hook, renders UI. Exported from `src/widgets/index.ts`.\n- `packages/react-instantsearch/src/ui/<Pascal>.tsx` — local presentational component, only when the markup isn't already in the shared `instantsearch-ui-components` package.\n\nSo adding/changing a React widget often touches `react-instantsearch-core` (hook) **and** `react-instantsearch` (component) — keep them in sync.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch build\nyarn workspace react-instantsearch-core build\nyarn jest packages/react-instantsearch/src/widgets/__tests__/<Pascal>   # from repo root\n```\n\n- Uses React 19 / `@testing-library/react` for tests, co-located in `__tests__/`.\n- Cross-flavor behavior lives in `tests/common/widgets/<name>/`; register it in this package's `common-widgets.test.*`.\n- Prefer shared UI from `instantsearch-ui-components` (consume `create<Name>Component` with React's `createElement`) over new local `ui/` components. **Creating or changing a shared component is the `instantsearch-ui-components` package's job** — do that there, then consume it here. Add a local `src/ui/` component only when the markup truly isn't shareable.\n","packages/react-instantsearch/README.md":"<p align=\"center\">\n  <a href=\"https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/\">\n    <img alt=\"React InstantSearch\" src=\"https://github.com/algolia/instantsearch/blob/master/.github/react-instantsearch-banner.png?raw=true\">\n  </a>\n</p>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch](#react-instantsearch)\n  - [Why](#why)\n  - [Installation](#installation)\n  - [Getting started](#getting-started)\n  - [Documentation](#documentation)\n  - [Playground](#playground)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch\n\nReact InstantSearch is an open-source React library that lets you create an instant search result experience using [Algolia][algolia-website]’s search API. It is part of the InstantSearch family:\n\n**React InstantSearch** | [InstantSearch.js][instantsearch-github] | [Vue InstantSearch][instantsearch-github] | [InstantSearch Android][instantsearch-android-github] | [InstantSearch iOS][instantsearch-ios-github]\n\n## Why\n\nYou should be using React InstantSearch if you want to:\n\n- Design search experiences with best practices\n- Customize your components at will\n- Follow React principles\n\nNote: If you are working with React Native, or otherwise do not use the DOM, check out `react-instantsearch-core` instead.\n\n## Installation\n\nReact InstantSearch is available on the npm registry. It relies on [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) to communicate with Algolia APIs.\n\n```sh\nyarn add algoliasearch react-instantsearch\n# or\nnpm install algoliasearch react-instantsearch\n```\n\n## Getting started\n\nUsing React InstantSearch is as simple as adding these components to your app:\n\n```javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { liteClient as algoliasearch } from 'algoliasearch/lite';\nimport { InstantSearch, SearchBox, Hits } from 'react-instantsearch';\n\nconst searchClient = algoliasearch(\n  'latency',\n  '6be0576ff61c053d5f9a3225e2a90f76'\n);\n\nconst App = () => (\n  <InstantSearch indexName=\"bestbuy\" searchClient={searchClient}>\n    <SearchBox />\n    <Hits />\n  </InstantSearch>\n);\n```\n\n<p align=\"center\">\n  <a href=\"https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/default-theme\" title=\"Edit on CodeSandbox\">\n    <img alt=\"Edit on CodeSandbox\" src=\"https://codesandbox.io/static/img/play-codesandbox.svg\">\n  </a>\n</p>\n\nTo learn more about the library, follow the [getting started guide][doc-getting-started].\n\n## Documentation\n\nThe documentation is available on [algolia.com/doc][doc].\n\n## Playground\n\nYou can get to know React InstantSearch on [this playground][doc-playground].\n\nStart by [adding components][doc-getting-started] and tweaking the display. Once you get more familiar with the library, you can learn more advanced concepts in [our guides][doc-guides].\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[doc]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/\n[doc-getting-started]: https://www.algolia.com/doc/guides/building-search-ui/getting-started/react/\n[doc-guides]: https://www.algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react/\n[doc-playground]: https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/default-theme\n[algolia-website]: https://www.algolia.com/\n[instantsearch-github]: https://github.com/algolia/instantsearch\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","packages/vue-instantsearch/CLAUDE.md":"# vue-instantsearch\n\nVue 2 **and** Vue 3 bindings (single source, built for both). Components wrap the `instantsearch.js` connectors — the logic isn't reimplemented here.\n\n## Layout\n\n- `src/components/<Pascal>.vue` (SFC) or `.js` (render function) — the component wrapping a connector. Exported from `src/widgets.js`.\n- `src/mixins/` — shared connector-wiring logic reused across components.\n- To reuse shared markup, import the `create<Name>Component` factory from `instantsearch-ui-components` and render it inside a **`renderCompat`** render function, passing Vue's `h` as `createElement` — e.g. `render: renderCompat((h) => h(createHitsComponent({ createElement: h }), { … }))` (see `src/components/Hits.js`). `renderCompat` (`src/util/vue-compat/`) is the Vue 2/3 render-fn shim. **Creating or changing the shared component itself is the `instantsearch-ui-components` package's job** — do that there, then consume the factory here.\n\n## Working here\n\n```bash\nyarn workspace vue-instantsearch build\nyarn jest packages/vue-instantsearch/src/components/__tests__/<Pascal>   # from repo root\n```\n\n- Vue 2/3 dual support: run `./scripts/prepare-vue3.js` (root `yarn prepare-vue3`) when switching the working tree to Vue 3. Don't assume one Vue version — changes must work for both.\n- Cross-flavor behavior lives in `tests/common/widgets/<name>/`; register it in this package's `common-widgets.test.*`.\n","packages/vue-instantsearch/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Vue InstantSearch](#vue-instantsearch)\n- [Documentation](#documentation)\n- [Installation](#installation)\n- [Troubleshooting](#troubleshooting)\n- [Contributing, dev, release](#contributing-dev-release)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n[![Vue InstantSearch logo][logo]][website]\n\nInstantSearch projects: **Vue InstantSearch** | [InstantSearch.js][instantsearch-github] | [React InstantSearch][instantsearch-github] | [InstantSearch Android][instantsearch-android-github] | [InstantSearch iOS][instantsearch-ios-github].\n\n## Vue InstantSearch\n\n> ⚡ Lightning-fast search for Vue.js apps\n\nBuilt by [Algolia][algolia-website].\n\nThis repository holds the code for the Vue InstantSearch project.\n\n## Documentation\n\nThere's a dedicated documentation available at [www.algolia.com/doc/guides/building-search-ui/getting-started/vue][website].\n\n## Installation\n\nVue InstantSearch is available in the npm registry. Install it:\n\n```sh\n# with npm\nnpm install --save vue-instantsearch\n\n# with yarn\nyarn add vue-instantsearch\n```\n\nTo learn more about the usage, follow our [getting started guide][getting-started-guide].\n\n## Troubleshooting\n\nEncountering an issue? Before reaching out to support, we recommend heading to our [FAQ](https://www.algolia.com/doc/guides/building-search-ui/troubleshooting/faq/vue/) where you will find answers for the most common issues and gotchas with the library.\n\n## Contributing, dev, release\n\nWe welcome all contributors, from casual to regular. You are only one command away to start the developer environment, [read our CONTRIBUTING guide](CONTRIBUTING.md).\n\n[logo]: vue-instantsearch-readme.png\n[website]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/vue/\n[getting-started-guide]: https://www.algolia.com/doc/guides/building-search-ui/getting-started/vue/\n[algolia-website]: https://www.algolia.com/\n[instantsearch-github]: https://github.com/algolia/instantsearch\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n"},"files":{".claude/commands/expose-option.md":"---\ndescription: Thread a new option on an existing connector through all three flavors (JS, React, Vue) + common tests\nargument-hint: <connector> <optionName> [short description of what it does]\n---\n\nYou are exposing a new option through the InstantSearch fan-out. The architecture is **connector (logic) → flavor wrapper → shared UI**, so the option is defined once in the connector and surfaced by each flavor.\n\n**Input:** `$ARGUMENTS`\n\n- First token = the connector (e.g. `connectRefinementList` or just `refinementList`).\n- Second token = the new option name (e.g. `clearOnChange`).\n- Remaining text = a short description of the behavior, if given.\n\nIf the connector or option name is missing or ambiguous, ask before changing files. If the behavior isn't specified, ask what the option should do — do **not** guess at semantics.\n\nThis is a checklist for a cross-flavor option rollout. The connector is the single source of truth; React and Vue only _wrap_ it, so do the connector first, then the wrappers.\n\n## Steps\n\n1. **Scope the contract first.** Read the connector `packages/instantsearch.js/src/connectors/<name>/connect<Pascal>.ts` and its JS widget `packages/instantsearch.js/src/widgets/<name>/<name>.tsx`. Confirm the option doesn't already exist and settle the exact semantics + the option's type signature. This is the contract every flavor must match.\n\n2. **Connector first** (in `packages/instantsearch.js`):\n   - Add the option to the connector's widget-params type with a TSDoc comment.\n   - Implement the behavior in the lifecycle (`init`/`render`/`getWidgetSearchParameters`/etc.).\n   - Surface it on the **typed render state** if consumers need it (untyped render state is an oxlint error).\n   - Thread it through the JS widget. Keep it framework-agnostic — no React/Vue/DOM-framework code in the connector.\n\n   For a presentational-only variant, **reuse the existing connector** with a distinct `$$widgetType` — never fork connector logic.\n\n3. **If the option changes shared markup/layout** (not just behavior), update the shared `create<Name>Component` in `packages/instantsearch-ui-components` _before_ wiring the flavors — its output is the contract they consume.\n\n4. **Thread it through the wrappers** (independent of each other once the connector is settled):\n   - **React** — the hook `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts` and the component `packages/react-instantsearch/src/widgets/<Pascal>.tsx`, kept in sync and typed.\n   - **Vue** — `packages/vue-instantsearch/src/components/<Pascal>.{vue,js}` (and `src/mixins/` if wiring lives there), working for **both Vue 2 and Vue 3**.\n\n5. **Tests.** Cross-flavor behavior → add/extend `tests/common/connectors/<name>/` (or `.../widgets/<name>/`) and register it in each flavor's `common-*.test.*` (see `tests/common/README.md`); flavor-specific assertions stay in each package's co-located `__tests__/`. If browser-level behavior is affected, add an e2e spec (see `.claude/rules/e2e.md`).\n\n6. **Verify.** Confirm all three flavors expose the same option with consistent naming/types, then:\n   ```bash\n   yarn jest packages/instantsearch.js/src/connectors/<name>\n   yarn jest <the react/vue paths you touched>\n   yarn type-check\n   yarn lint:changed\n   ```\n   (Or run `/preflight`.)\n\n## Notes\n\n- Why this order: the connector is the contract; React and Vue only _wrap_ it, so they're independent of each other and safe to do once the connector is settled.\n- Don't hand-edit changelogs (Ship.js generates them). Commit message: `feat(<widget>): add <optionName> option`.\n",".claude/commands/preflight.md":"---\ndescription: Run the pre-push checklist — lint, types, relevant tests, and a Conventional-Commit sanity check\nallowed-tools: Bash(yarn lint:changed), Bash(yarn lint:fix), Bash(yarn type-check), Bash(yarn type-check:*), Bash(yarn jest:*), Bash(git status:*), Bash(git diff:*), Bash(git log:*)\n---\n\nRun the InstantSearch pre-push checks against the **current changes** and report a concise pass/fail summary. Don't fix anything silently — surface failures and propose fixes.\n\n## Steps\n\n1. **Scope the change.** `git status` + `git diff --name-only` (and vs. the base branch if on a feature branch) to see which packages/files are touched.\n\n2. **Lint the changed files:** `yarn lint:changed`. If it fails, show the violations; offer `yarn lint:fix` for auto-fixable ones (note that oxlint bans `for-in`/`for-of`/`async` and implicit `any` in library code — those need manual fixes).\n\n3. **Type-check:** `yarn type-check`. If any touched code interacts with the legacy algoliasearch versions, also run `yarn type-check:v3` / `yarn type-check:v4`.\n\n4. **Run the relevant tests** — not the whole suite. Map changed files to their `yarn jest <path>` (e.g. a connector → `yarn jest packages/instantsearch.js/src/connectors/<name>`; a React widget → its `__tests__` path). If a connector's cross-flavor behavior changed, include the matching `tests/common/` path.\n\n5. **Conventional-Commit sanity check.** Look at staged changes / recent commits and confirm the message fits `type(scope): description` (scope = widget/connector or topic like `deps`/`ci`). Flag if it doesn't; suggest a corrected message. Reference issues with `fix #1234` in the body when applicable.\n\n## Output\n\nReport a short checklist:\n\n- ✅/❌ lint:changed\n- ✅/❌ type-check (+ v3/v4 if run)\n- ✅/❌ tests (list the paths run)\n- ✅/❌ commit message\n\nFor any ❌, show the failing output and the smallest fix. Don't run the full `yarn test` or `yarn build` unless asked — this is the fast pre-push loop.\n\nRun the checks directly. If a check fails, fix it in the package that owns the failing file before re-running.\n",".claude/rules/e2e.md":"---\npaths:\n  - 'tests/e2e/**'\n  - '.github/workflows/e2e.yml'\n  - '.github/workflows/ci.yml'\n  - 'website/serve.json'\n  - 'examples/*/e-commerce*/**'\n  - 'packages/react-instantsearch-nextjs/playwright.config.ts'\n  - 'packages/react-instantsearch-nextjs/__tests__/e2e/**'\n  - 'packages/react-instantsearch-router-nextjs/playwright.config.ts'\n  - 'packages/react-instantsearch-router-nextjs/__tests__/e2e/**'\n---\n\n# E2E Testing Guide\n\n## Test Locations and Frameworks\n\n| Location | Framework | Purpose |\n| --- | --- | --- |\n| `tests/e2e/playwright/` | Playwright | Main e2e suite for all InstantSearch flavors |\n| `tests/e2e/` (wdio files) | WebdriverIO | IE11 tests only (via Sauce Labs) |\n| `packages/react-instantsearch-nextjs/__tests__/e2e/` | Playwright | App Router Next.js e2e tests |\n| `packages/react-instantsearch-router-nextjs/__tests__/e2e/` | Playwright | Pages Router Next.js e2e tests |\n\n`.github/workflows/e2e.yml` runs on a weekday schedule and is called from `ci.yml` on pushes to master, never on pull requests. Run the suites locally before you push.\n\n## Running Tests Locally\n\n### Main E2E Suite (Playwright)\n\n**Prerequisites:** Examples must be built first:\n\n```bash\nyarn website:examples\n```\n\n```bash\n# Run all Playwright tests (all flavors, chromium + firefox)\nyarn test:e2e\n\n# Run tests for a specific flavor\nE2E_FLAVOR=react yarn test:e2e\n\n# Run tests for a specific browser (faster local dev)\nE2E_BROWSER=chromium yarn test:e2e\n\n# Combine both for fastest local iteration\nE2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n\n# Run tests with UI mode for debugging\nyarn workspace @instantsearch/e2e-tests test:playwright --ui\n```\n\n### Next.js Package Tests\n\n```bash\n# App Router (react-instantsearch-nextjs)\nyarn workspace react-instantsearch-nextjs test:e2e\n\n# Pages Router (react-instantsearch-router-nextjs)\nyarn workspace react-instantsearch-router-nextjs test:e2e\n\n# Run with list reporter (outputs to terminal instead of opening browser)\nyarn workspace react-instantsearch-nextjs test:e2e --reporter=list\nyarn workspace react-instantsearch-router-nextjs test:e2e --reporter=list\n```\n\n### IE11 Tests (Requires Sauce Labs credentials)\n\n```bash\n# Set environment variables first\nexport SAUCE_USERNAME=your_username\nexport SAUCE_ACCESS_KEY=your_access_key\n\nyarn test:e2e:ie11\n```\n\n## Writing Playwright Tests\n\n### Test Structure\n\n```typescript\nimport { test, expect } from '../fixtures';\n\ntest.describe('feature name', () => {\n  test('does something specific', async ({ page, helpers }) => {\n    // Use '.' to navigate to baseURL, NOT '/' (which goes to root)\n    await page.goto('.');\n\n    // Use helpers for common InstantSearch interactions\n    await helpers.clickRefinementListItem('Apple');\n    await helpers.setSearchBoxValue('macbook');\n\n    // Use Playwright's built-in assertions\n    await expect(page.locator('.ais-Hits-item')).toHaveCount(16);\n  });\n});\n```\n\n### Available Helper Methods\n\nThe `helpers` fixture provides these methods. Full signatures in `tests/e2e/playwright/fixtures.ts`.\n\n- **RefinementList:** `clickRefinementListItem(label)`, `getSelectedRefinementListItem()`\n- **SearchBox:** `setSearchBoxValue(value)`, `getSearchBoxValue()`\n- **Hits:** `getHitsTitles()`\n- **HierarchicalMenu:** `clickHierarchicalMenuItem(label)`, `getSelectedHierarchicalMenuItems()`\n- **RangeSlider:** `dragRangeSliderLowerBoundTo(value)`, `dragRangeSliderUpperBoundTo(value)`, `getRangeSliderLowerBoundValue()`, `getRangeSliderUpperBoundValue()`\n- **Pagination:** `clickPage(number)`, `clickNextPage()`, `clickPreviousPage()`, `getCurrentPage()`\n- **ToggleRefinement:** `clickToggleRefinement()`, `getToggleRefinementStatus()`\n- **RatingMenu:** `clickRatingMenuItem(label)`, `getSelectedRatingMenuItem()`. `label` is the link's `aria-label`, such as `4 & up`.\n- **SortBy:** `setSortByValue(label)`, `getSortByValue()`\n- **HitsPerPage:** `setHitsPerPage(label)`, `getHitsPerPage()`\n- **ClearRefinements:** `clickClearRefinements()`\n- **Any selector:** `waitForElement(selector)`, `getTextFromSelector(selector)`\n\n### Best Practices\n\n1. **Use helpers for InstantSearch interactions** - They handle waiting for URL changes automatically\n2. **Prefer Playwright's auto-waiting assertions** - Use `await expect(locator).toHaveText('...')` instead of manual waits\n3. **Avoid `page.waitForTimeout()`** - Use proper waits like `waitForURL()` or `expect.poll()`\n4. **Use locator chaining** - `page.locator('.parent').locator('.child')`\n5. **Use `'.'` for baseURL navigation** - `page.goto('.')` navigates to the baseURL path; `page.goto('/')` goes to the root domain (common mistake)\n6. **Prefer relative paths** - For routes like `search/category/?query=test`, use paths without leading `/`\n7. **Avoid hardcoded test data** - The Algolia index data can change. Instead of checking exact product names, verify behavior (e.g., results changed, count > 0, URL updated)\n8. **Wait for results after navigation** - After pagination or filter changes, wait for new results to load before asserting\n9. **Don't use `scrollIntoViewIfNeeded()`** - Playwright auto-scrolls elements into view before clicking\n10. **Don't add redundant waits after URL waits** - If you wait for URL change, the UI has already updated; no need to also wait for element visibility\n\n## Flavor System\n\nThe main e2e suite tests multiple InstantSearch flavors:\n\n- `js` - instantsearch.js with vanilla JS\n- `js-umd` - instantsearch.js UMD build\n- `react` - React InstantSearch\n- `vue` - Vue InstantSearch\n\nTests run against example apps in `website/examples/{flavor}/e-commerce/`.\n\nTo run a single flavor:\n\n```bash\nE2E_FLAVOR=react yarn test:e2e\n```\n\n## IE11 Considerations\n\nIE11 tests are kept in WebdriverIO because Playwright doesn't support IE11. These tests:\n\n- Only run `js` and `js-umd` flavors\n- Require Sauce Labs for remote IE11 browser\n- Use the same test specs in `tests/e2e/specs/` and helpers in `tests/e2e/helpers/`\n\n## Debugging\n\n### Playwright\n\n```bash\n# Run with headed browser\nyarn workspace @instantsearch/e2e-tests test:playwright --headed\n\n# Run with UI mode (interactive debugging)\nyarn workspace @instantsearch/e2e-tests test:playwright --ui\n\n# Run specific test file\nyarn workspace @instantsearch/e2e-tests test:playwright specs/brand-and-query.spec.ts\n\n# Debug mode with inspector\nPWDEBUG=1 yarn workspace @instantsearch/e2e-tests test:playwright\n```\n\n### View Test Reports\n\nAfter running tests, open the HTML report:\n\n```bash\nnpx playwright show-report tests/e2e/playwright/playwright-report\n```\n\n## Troubleshooting\n\n### Tests timeout waiting for elements\n\n1. **Check if examples are built**: Run `yarn website:examples` before running tests\n2. **Check if the server starts**: The webServer uses port 3456 (not 5000, which conflicts with macOS AirPlay)\n3. **Check navigation**: Use `page.goto('.')` not `page.goto('/')` to navigate to baseURL\n\n### Port conflicts on macOS\n\nOn macOS, port 5000 is used by AirPlay Receiver (ControlCenter). The tests use port 3456 instead. If you need to change the port, update:\n\n- `tests/e2e/playwright/playwright.config.ts` (both `baseURL` and `webServer.command/url`)\n\n### SPA routing not working\n\nThe e-commerce examples use History API routing (e.g., `/search/Appliances/`). The `website/serve.json` file configures rewrites for these routes. If you add new SPA routes, update this file:\n\n```json\n{\n  \"rewrites\": [\n    {\n      \"source\": \"/examples/js/e-commerce/search/**\",\n      \"destination\": \"/examples/js/e-commerce/index.html\"\n    }\n  ]\n}\n```\n\n### Tests are flaky with multiple workers\n\nThe static server can handle 2 parallel workers. All configs use 2 workers for both CI and local runs.\n\nThe main suite retries once locally and twice in CI. The Next.js suites retry twice in CI and not at all locally.\n\n### Multiple elements matched\n\nIf you see \"strict mode violation\" errors about multiple elements:\n\n- SearchBox selectors are scoped to the header to avoid matching RefinementList search inputs\n- Use more specific selectors when targeting widgets that may appear multiple times\n\n### Different HTML structures across flavors\n\nThe JS and React examples have different HTML structures:\n\n- **JS examples**: Use `id=\"header\"` and `data-widget=\"searchbox\"` attributes\n- **React examples**: Use `className=\"header\"` (class instead of id) and render SearchBox directly inside header\n\nThe fixtures account for this with combined selectors like:\n\n```typescript\n'#header .ais-SearchBox [type=search], .header > .ais-SearchBox [type=search], [data-widget=\"searchbox\"] .ais-SearchBox [type=search]';\n```\n\nIf you add new selectors, ensure they work across all flavors.\n\n## Migration Notes: WebDriverIO to Playwright\n\nWhen migrating tests from WebDriverIO to Playwright, be aware of these differences:\n\n### URL Waiting\n\n- **WebDriverIO**: Used custom `waitForUrl()` that polls `browser.getUrl()` until match\n- **Playwright**: Use `await expect(page).toHaveURL(url)` which has built-in retrying\n\n### Element Selection\n\n- **WebDriverIO**: `$('.ais-Hits-item')` returns first match, clicking works on container elements\n- **Playwright**: `page.locator('.ais-Hits-item').first()` - clicking on container may not hit child link elements. Use `.locator('a')` to target links explicitly:\n\n  ```typescript\n  // WebDriverIO\n  const link = await $('.ais-Hits-item');\n  await link.click();\n\n  // Playwright - click the link inside the hit\n  const link = page.locator('.ais-Hits-item a').first();\n  await link.click();\n  ```\n\n### Browser Back Navigation\n\n- **WebDriverIO**: `browser.back()` worked reliably for SPA navigation\n- **Playwright**: `page.goBack()` works but may require `slowMo` in headless mode for Next.js App Router dynamic routes\n\n### Headless Mode Timing Issues\n\nPlaywright's headless mode runs faster than headed mode, which can cause timing issues with SPA navigation and state updates. For Next.js App Router tests involving browser back/forward navigation with dynamic routes, add `slowMo` at the test file level (not globally, to avoid slowing down all tests):\n\n```typescript\nimport { test, expect } from '@playwright/test';\n\n// slowMo is required for browser back/forward navigation tests in headless mode\ntest.use({ launchOptions: { slowMo: 500 } });\n\ntest.describe('browser back/forward buttons', () => {\n  // tests that use page.goBack() or page.goForward()\n});\n```\n\nThis gives the browser enough time to process JavaScript events like popstate handlers used by InstantSearch routing, while keeping other tests fast.\n",".claude/skills/port-widget/SKILL.md":"---\nname: port-widget\ndescription: Port or introduce an InstantSearch widget or connector-driven feature across this monorepo's JavaScript, React, and Vue flavors. Use when the task is to add a missing wrapper around an existing connector, bring a widget from one flavor to another, or implement a new shared widget end-to-end in `instantsearch.js`, `react-instantsearch`, and `vue-instantsearch`. Triggers include requests like \"port X to vue\", \"add Y to react-instantsearch\", \"make Z available in all flavors\", or \"implement the missing InstantSearch wrapper for this connector\".\n---\n\n# Port InstantSearch Widgets Across Flavors\n\n## Start with the repo audit\n\n- Run `python3 scripts/audit_widget_coverage.py <widget-kebab-name>` from this skill folder before editing.\n- Use `--repo /path/to/instantsearch` if your current working directory is not inside the InstantSearch repo.\n- Treat placeholder Vue failures in `packages/vue-instantsearch/src/__tests__/common-widgets.test.js` or `common-connectors.test.js` as evidence that the connector exists but the Vue wrapper still needs work.\n\n## Layer map\n\n- Connector: `packages/instantsearch.js/src/connectors/<widget>/connect<Pascal>.ts`\n- JS widget: `packages/instantsearch.js/src/widgets/<widget>/<widget>.tsx`\n- React hook: `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts`\n- React widget: `packages/react-instantsearch/src/widgets/<Pascal>.tsx`\n- Optional React UI: `packages/react-instantsearch/src/ui/<Pascal>.tsx`\n- Vue wrapper: `packages/vue-instantsearch/src/components/<Pascal>.vue` or `.js`\n- Shared widget tests: `tests/common/widgets/<widget>/`\n- Shared connector tests: `tests/common/connectors/<widget>/`\n\n## Variant widgets\n\nSome widgets reuse another widget's connector with different defaults or UI. For example, `menuSelect` uses `connectMenu`/`useMenu` (not a dedicated `connectMenuSelect`). The audit will show `no` for connector and hook rows — this is expected. The `$$widgetType` still differs (`ais.menuSelect` vs `ais.menu`). When porting a variant widget, skip connector/hook creation and reuse the existing hook directly in the widget file.\n\nKnown variants: `menuSelect` → `connectMenu`/`useMenu`.\n\n## Workflow\n\n1. Decide the scope.\n   - Existing connector, missing wrapper: keep the connector API unchanged and port only the wrapper plus wrapper tests.\n   - Variant widget (shared connector, different UI): skip connector/hook creation; reuse the existing hook and set a distinct `$$widgetType`.\n   - Missing connector or changed render state: start in `instantsearch.js`, then update every flavor and both common test suites.\n   - Vue port for a newer recommendation, chat, or filter-suggestions feature: inspect `Hits.js`, `Highlighter.js`, `DynamicWidgets.js`, and `util/vue-compat.js` before designing the wrapper.\n2. Match a real precedent.\n   - Pick one close widget in the target flavor and one close widget in another flavor.\n   - Reuse the same prop names, slot or component escape hatches, `$$widgetType`, and test style.\n3. Build from the bottom up.\n   - Connector exports belong in `packages/instantsearch.js/src/connectors/index.ts`.\n   - JS widget exports belong in `packages/instantsearch.js/src/widgets/index.ts`.\n   - React hook exports belong in `packages/react-instantsearch-core/src/index.ts`.\n   - React widget exports belong in `packages/react-instantsearch/src/widgets/index.ts`; `packages/react-instantsearch/src/index.ts` already re-exports widgets.\n   - Vue exports belong in `packages/vue-instantsearch/src/widgets.js`; `src/instantsearch.js` and the plugin re-export and register from there automatically.\n4. Choose the right sharing model.\n   - JS and React: prefer `instantsearch-ui-components` when the markup can be shared.\n   - React: create `src/ui/<Pascal>.tsx` whenever the widget has no shared factory in `instantsearch-ui-components`. This includes simple widgets like `MenuSelect` (a plain `<select>`) — `src/ui/` is for all React-rendered markup, not only complex cases.\n   - Vue: use `.vue` SFCs for slot-heavy markup and `.js` render functions with `renderCompat` when reusing `instantsearch-ui-components`.\n5. Wire tests before finishing.\n   - Update `tests/common/widgets/<widget>/` whenever the wrapper behavior changes.\n   - Update `tests/common/connectors/<widget>/` whenever connector params or render state change.\n   - Register the suite in each flavor's `common-widgets.test.*` and `common-connectors.test.*`.\n   - Replace any `throw new Error('X is not supported in ...')` placeholder with real setup code in the target flavor's `common-widgets.test.*`.\n   - Remove the corresponding `skippedTests` entry in `testOptions` for that widget.\n   - For React: always add the widget to the switch in `packages/react-instantsearch/src/widgets/__tests__/__utils__/all-widgets.tsx` with the required minimum props.\n6. Check examples only when the widget is user-facing.\n   - Search existing examples first. Recommendation, chat, and query-suggestion widgets already live in getting-started or query-suggestions examples, not only the e-commerce apps.\n   - Add to `examples/*/e-commerce` only when the widget fits the shared storefront UX or existing Playwright coverage.\n\n## Reminders\n\n- Keep `$$widgetType` aligned across flavors.\n- Do not invent new Vue patterns; match `createWidgetMixin`, `createSuitMixin`, scoped slots, and `renderCompat`.\n- Do not add memoization hooks in React unless an adjacent widget uses them for the same reason.\n\n### Cross-package API migration checks\n\n- Update all affected layers in one pass when moving or splitting behavior: wrapper APIs, shared UI components, layout/type contracts, exports, and tests. Wrapper-only edits create cross-package type drift.\n- If you remove a required prop or render path, either delete it end-to-end or make downstream contracts optional in the same change; partial migrations often fail during declaration builds.\n- Before finishing, grep for legacy names (old props, options, class names, template keys, translations keys) to catch leftovers across flavors.\n- Keep control contracts explicit and consistent when behavior is externally controlled (e.g., imperative handles or widget methods), so external integrations do not depend on internal layout details.\n- Do not add ad-hoc top-level markdown change logs unless requested; keep source of truth in code, tests, and existing docs.\n- Always run monorepo build validation (`yarn build --ignore='example*'`) after cross-package API changes; it catches declaration and export drift that local tests may miss.\n\n## References\n\n- JS wrappers: [references/js-flavor.md](references/js-flavor.md)\n- React wrappers: [references/react-flavor.md](references/react-flavor.md)\n- Vue wrappers: [references/vue-flavor.md](references/vue-flavor.md)\n- Testing and examples: [references/testing.md](references/testing.md)\n",".claude/skills/port-widget/agents/openai.yaml":"interface:\n  display_name: 'Port InstantSearch Widget'\n  short_description: 'Port widgets across InstantSearch flavors'\n  default_prompt: 'Use $port-widget to port a widget or connector-driven feature across the InstantSearch JavaScript, React, and Vue packages in this repo.'\n\npolicy:\n  allow_implicit_invocation: true\n",".claude/skills/port-widget/references/js-flavor.md":"# JavaScript Flavor (`instantsearch.js`)\n\n## Reach for these precedents first\n\n- Shared UI via `instantsearch-ui-components`: `hits`, `related-products`, `trending-items`, `filter-suggestions`\n- Older Preact widget with templates and CSS helpers: `refinement-list`, `menu`, `pagination`\n\n## File layout\n\n```text\npackages/instantsearch.js/src/widgets/<widget>/\n  <widget>.tsx\n  defaultTemplates.tsx        # only when the widget exposes templates\n  __tests__/<widget>-test.tsx # optional flavor-specific tests\n```\n\n## Wrapper pattern\n\n- Import the connector from `../../connectors/<widget>/connect<Pascal>`.\n- Build a `createRenderer(...)` helper that closes over `containerNode`, classes, templates, and any normalized props.\n- Use `getContainerNode(container)` and throw via `createDocumentationMessageGenerator` when `container` is missing.\n- Mount with `render(<UiComponent ... />, containerNode)` and unmount with `render(null, containerNode)`.\n- Return `$$widgetType: 'ais.<camelName>'`.\n\n## UI choice\n\n- Prefer `instantsearch-ui-components` when the React and JS widgets should share markup.\n- Keep local JSX only when the JS widget still depends on legacy template plumbing or DOM structure that is not shared elsewhere.\n- If templates are supported, follow `TemplateComponent` and `prepareTemplateProps` patterns from a close sibling widget rather than inventing a new API.\n\n## Registration checklist\n\n- Export the connector from `packages/instantsearch.js/src/connectors/index.ts`.\n- Export the widget from `packages/instantsearch.js/src/widgets/index.ts`.\n",".claude/skills/port-widget/references/react-flavor.md":"# React Flavor (`react-instantsearch`)\n\n## Package split\n\n- `packages/react-instantsearch-core`: hooks that wrap `instantsearch.js` connectors\n- `packages/react-instantsearch`: public widgets and optional React-only UI components\n\n## Hook pattern\n\nFile: `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts`\n\n- Import the connector from `instantsearch.js/es/connectors/<widget>/connect<Pascal>`.\n- Import `useConnector` and `AdditionalWidgetProperties` from `../hooks/useConnector`.\n- Export `Use<Pascal>Props = <Pascal>ConnectorParams`.\n- Pass `additionalWidgetProperties` through to `useConnector(...)`.\n\n## Widget pattern\n\nChoose the closest precedent before writing code:\n\n- Shared UI component wrapper: `Hits.tsx`, `RelatedProducts.tsx`, `TrendingItems.tsx`, `FilterSuggestions.tsx`\n- React-only presentational UI in `src/ui`: `SearchBox.tsx`, `RangeInput.tsx`, `RefinementList.tsx`, `Menu.tsx`, `MenuSelect.tsx`, `SortBy.tsx`\n\n### If the UI is shared via `instantsearch-ui-components`\n\n- Instantiate the factory with `createElement as Pragma` and `Fragment`.\n- Keep connector params separate from UI-only props.\n- Pass `{ $$widgetType: 'ais.<camelName>' }` as the second argument to the hook.\n- Use `useInstantSearch()` only when the UI needs search status or other top-level state.\n- Only add `useMemo` or `useCallback` when an adjacent widget uses them to stabilize component props passed into a shared UI factory.\n\n### If no shared factory exists (React-only UI)\n\n- Create `packages/react-instantsearch/src/ui/<Pascal>.tsx` for the presentational component. This applies to all widgets without a shared factory — even simple ones like `MenuSelect` (a plain `<select>`).\n- Keep the widget file focused on mapping hook state and event handlers into the UI component.\n\n### Variant widgets (shared connector)\n\nSome widgets reuse another widget's hook instead of having a dedicated one. For example, `MenuSelect` uses `useMenu` (not `useMenuSelect`). In this case:\n\n- Import the existing hook directly in the widget file.\n- Pick only the relevant connector params (e.g., exclude `showMore`/`showMoreLimit` for `MenuSelect`).\n- Set a distinct `$$widgetType` (e.g., `'ais.menuSelect'`).\n\n## Registration checklist\n\n- Export the hook from `packages/react-instantsearch-core/src/index.ts` (skip for variant widgets that reuse an existing hook).\n- Export the widget from `packages/react-instantsearch/src/widgets/index.ts`.\n- If the widget is public, `packages/react-instantsearch/src/index.ts` already re-exports it through `./widgets`.\n- Add the widget to the switch in `packages/react-instantsearch/src/widgets/__tests__/__utils__/all-widgets.tsx` with required minimum props (e.g., `attribute=\"brand\"`).\n- In `packages/react-instantsearch/src/__tests__/common-widgets.test.tsx`:\n  - Replace the `throw new Error('X is not supported in React InstantSearch')` with real setup code.\n  - Remove the `skippedTests` entry for the widget in `testOptions`.\n",".claude/skills/port-widget/references/testing.md":"# Testing and Examples\n\n## Start with the common suites\n\n### Wrapper or public widget change\n\n- Update `tests/common/widgets/<widget>/`.\n- Register or unskip the suite in each flavor's `common-widgets.test.*`:\n  - `packages/instantsearch.js/src/__tests__/common-widgets.test.tsx`\n  - `packages/react-instantsearch/src/__tests__/common-widgets.test.tsx`\n  - `packages/vue-instantsearch/src/__tests__/common-widgets.test.js`\n- For previously unsupported widgets: replace the `throw new Error('X is not supported in ...')` placeholder with real setup code, and remove the `skippedTests` entry in `testOptions`.\n\n### Connector or hook API change\n\n- Update `tests/common/connectors/<widget>/`.\n- Register or unskip the suite in:\n  - `packages/instantsearch.js/src/__tests__/common-connectors.test.tsx`\n  - `packages/react-instantsearch/src/__tests__/common-connectors.test.tsx`\n  - `packages/vue-instantsearch/src/__tests__/common-connectors.test.js`\n\n## Flavor-specific tests\n\n- JavaScript: `packages/instantsearch.js/src/widgets/<widget>/__tests__/`\n- React: `packages/react-instantsearch/src/widgets/__tests__/`\n- Vue: colocated component or package tests under `packages/vue-instantsearch/src/__tests__/`\n\nAdd flavor-specific tests only for behavior that the shared suites do not cover, such as:\n\n- slot rendering differences in Vue\n- React-only UI plumbing\n- template or CSS-class edge cases in `instantsearch.js`\n- registry coverage such as React's `all-widgets` helpers\n\n## Shared cross-widget suites\n\n- Search `tests/common/shared/` and `tests/common/shared-composition/` when the port affects routing, searchable behavior, or cross-widget composition.\n- Only touch those suites when the new widget changes shared behavior rather than a single widget contract.\n\n## Examples\n\n- Search existing examples before editing; not every user-facing widget belongs in the e-commerce demos.\n- E-commerce apps live in:\n  - `examples/js/e-commerce/src/`\n  - `examples/react/e-commerce/`\n  - `examples/vue/e-commerce/src/`\n- Recommendation, chat, and query-suggestion features already appear in getting-started or query-suggestions examples for JS and React.\n\n## Vue-specific test timing\n\n### State delivery is async for some connectors\n\nMost connectors (e.g. `connectRefinementList`) deliver state synchronously during `mountApp` because the mock search client responds immediately. Newer connectors like `connectChat` deliver state asynchronously. The Vue test setup must account for this:\n\n```javascript\nmountApp({ render: ... }, container);\nawait nextTick();                                      // Vue tick\nawait new Promise((resolve) => setTimeout(resolve, 0)); // macrotask for async state\nawait nextTick();                                      // Vue tick to re-render\n```\n\n### Provide a custom `act` for Vue\n\nVue batches DOM updates asynchronously, unlike React which re-renders synchronously within event handlers. Tests that `await act(async () => { await wait(0); })` work because `act` wraps the wait. Provide a custom `act` in `testOptions` that flushes Vue's update queue:\n\n```javascript\ncreateXxxWidgetTests: {\n  act: async (cb) => {\n    await cb();\n    await nextTick();\n  },\n},\n```\n\nTests that click then immediately assert DOM changes (without `act`) will still fail in Vue. These are known limitations of the shared test suite's React-centric timing assumptions. Document them in the `testOptions` but avoid skipping the entire test suite.\n\n### Flavored test params\n\nFor `flavored` test suites (like Chat), `runTestSuites` extracts the flavor-specific params via `widgetParams[flavor]`. The Vue setup function receives only the Vue portion. Ensure the Vue params include all required connector params (e.g. `agentId`, `chat`) — empty `{}` will cause connector errors.\n\nWhen Vue params need component functions (tools with `layoutComponent`, custom `headerComponent`), use `import { h as vueH } from 'vue'` in the `.tsx` test file and return VNodes: `() => vueH('div', { class: 'foo' }, 'text')`.\n\n### skippedTests granularity\n\n`skippableDescribe` only skips entire describe blocks by name. Individual test names in `skippedTests` have no effect unless the test file uses `skippableTest`. The shared chat/options/templates/translations test files use plain `test()`, so individual test skipping is not available for Chat widget tests.\n\n## Useful commands\n\n```bash\n# Common widget/connector tests\nnpx jest packages/instantsearch.js/src/__tests__/common-widgets.test.tsx --no-coverage\nnpx jest packages/instantsearch.js/src/__tests__/common-connectors.test.tsx --no-coverage\nnpx jest packages/react-instantsearch/src/__tests__/common-widgets.test.tsx --no-coverage\nnpx jest packages/react-instantsearch/src/__tests__/common-connectors.test.tsx --no-coverage\nnpx jest packages/vue-instantsearch/src/__tests__/common-widgets.test.js --no-coverage\n\n# Run only tests matching a specific widget name\nnpx jest packages/react-instantsearch/src/__tests__/common-widgets.test.tsx --testNamePattern=\"MenuSelect\" --no-coverage\n\n# Flavor-specific tests\nnpx jest packages/react-instantsearch/src/widgets/__tests__ --no-coverage\nnpx jest packages/instantsearch.js/src/widgets/<widget> --no-coverage\n\n# Update snapshots after adding new exports\nnpx jest packages/react-instantsearch/src/widgets/__tests__ --no-coverage -u\n\n# E2E\nyarn website:examples\nE2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n```\n",".claude/skills/port-widget/references/vue-flavor.md":"# Vue Flavor (`vue-instantsearch`)\n\n## Current patterns in this repo\n\n- Slot-heavy SFCs: `RefinementList.vue`, `Menu.vue`, `Pagination.vue`\n- Render-function wrappers around shared UI components: `Hits.js`, `Highlighter.js`\n- Framework glue helpers: `mixins/widget.js`, `mixins/suit.js`, `util/vue-compat.js`\n\n## Choose the wrapper shape deliberately\n\n### Use a `.vue` SFC when\n\n- the widget exposes multiple named slots\n- the DOM structure is easiest to express in template syntax\n- the closest existing widget is already an SFC\n\n### Use a `.js` render function when\n\n- the widget reuses `instantsearch-ui-components`\n- the wrapper mostly adapts connector state into a shared UI factory\n- the widget needs `renderCompat(...)` or lower-level vnode control\n\n## Implementation rules\n\n- Import connectors from `instantsearch.js/es/connectors/index`.\n- Use `createWidgetMixin({ connector: ... }, { $$widgetType: 'ais.<camelName>' })`.\n- Use `createSuitMixin({ name: '<Pascal>' })` for BEM classes. When the widget delegates all rendering to a shared `createXxxComponent` factory, the suit mixin's `suit()` method goes unused but the `classNames` prop it provides is still convenient. Pass `this.classNames` directly to the shared component's `classNames` prop (semantic keys like `{ root, container }`, not BEM keys).\n- Expose connector params through a computed `widgetParams()` object.\n- When reusing shared UI factories, wrap the render function with `renderCompat(...)` and map `this.classNames` into the `classNames` prop expected by the shared component.\n- Prefer `getScopedSlot` or `getDefaultSlot` helpers over direct slot access when matching render-function components.\n- In render-function callbacks that reference connector state (e.g. `onSubmit`, `onInput`), read from `this.state.xxx` instead of destructured locals. Vue batches re-renders, so destructured values become stale between synchronous user interactions (type then click).\n\n## Registration checklist\n\n- Export the component from `packages/vue-instantsearch/src/widgets.js`.\n- Do not manually edit `src/plugin.js` or `src/instantsearch.js` for normal widget additions; they already derive registration and exports from `widgets.js`.\n- Search `packages/vue-instantsearch/src/__tests__/common-widgets.test.js` and `common-connectors.test.js` for placeholder \"not supported\" branches before deciding the widget is genuinely absent.\n\n## Missing-feature caution\n\n- Recommendation widgets, chat, and filter suggestions already have connector-level test placeholders in Vue, but several still throw explicit \"not supported\" errors at the widget layer.\n- If you port one of those widgets, remove the placeholder failure and replace it with real setup code plus the component implementation.\n","AGENTS.md":"CLAUDE.md","CLAUDE.md":"# InstantSearch monorepo\n\nSearch UI libraries for Algolia across three flavors (vanilla JS, React, Vue), sharing one connector + UI-component core. Yarn 4 workspaces + Lerna (independent versioning).\n\n## Repo map\n\n| Package | What it is |\n| --- | --- |\n| `packages/instantsearch.js` | **Core.** Vanilla JS library. **All connectors live here** (`src/connectors/`) and are shared by every flavor. |\n| `packages/react-instantsearch-core` | Headless React: hooks (`useSearchBox`, …) that wrap connectors. Framework-agnostic (web/RN). |\n| `packages/react-instantsearch` | React DOM widgets = hooks + UI from `instantsearch-ui-components`. |\n| `packages/vue-instantsearch` | Vue 2/3 components wrapping connectors. |\n| `packages/instantsearch-ui-components` | **Shared UI layer.** Framework-agnostic widget markup + `ais-*` classes via factory components (`create<Name>Component({ createElement, Fragment })`); reused by React directly, Vue via `renderCompat`, JS via the Preact renderer. Newer widgets get their layout here; older ones still define it in `instantsearch.js/src/components/`. |\n| `packages/algoliasearch-helper` | Low-level search-parameter/state manager underneath connectors. Mature, separately-versioned; **rarely the place for a change** — fix the connector instead. See its `CLAUDE.md`. |\n| `packages/instantsearch.css` | Default themes. |\n| `packages/react-instantsearch-nextjs` / `-router-nextjs` | Next.js App Router / Pages Router integrations. |\n| `packages/create-instantsearch-app`, `instantsearch-cli`, `instantsearch-codemods` | Scaffolding / CLI / migration tooling. |\n\nArchitecture in one line: **connector (logic, in instantsearch.js) → flavor wrapper (JS widget / React hook+component / Vue component) → shared UI components**. Change behavior in the connector; change markup in the UI layer.\n\n## Where work goes (per layer)\n\nBecause behavior lives in the connector and the flavors only wrap it, most changes have a natural home — and the per-package `CLAUDE.md` files carry each layer's conventions:\n\n| Work in… | Package |\n| --- | --- |\n| Connectors, JS widgets, runtime (`src/lib`), legacy Preact components (`src/components`), types | `packages/instantsearch.js` |\n| Shared framework-agnostic markup / `ais-*` classes | `packages/instantsearch-ui-components` |\n| React widgets/hooks + Next.js integrations | `react-instantsearch` / `react-instantsearch-core` / `*-nextjs` |\n| Vue components (Vue 2 **and** 3) | `vue-instantsearch` |\n\nFor a **cross-flavor change**, settle the contract in the connector first, then update the React and Vue wrappers, then the common tests. If the change is to **shared markup/layout** (not behavior), it lives in `instantsearch-ui-components` and the flavors consume the result — a class/structure change there ripples to all flavors and `instantsearch.css` at once.\n\n**Adding or surfacing a connector option is always cross-flavor work** — settle the contract in the connector first, then thread it through React **and** Vue, then add/extend common tests. Don't wire one flavor and stop. The **`/expose-option <connector> <option>`** command walks this recipe; **`/preflight`** runs the pre-push lint/types/tests/commit check; the `/port-widget` skill adds a brand-new widget across flavors.\n\n## Commands\n\nNode `20.19.0` (`.nvmrc`). Use Yarn (v4, pinned by `packageManager`).\n\n```bash\n# Build\nyarn build                      # all packages (lerna)\nyarn workspace <pkg> build      # single package, e.g. yarn workspace instantsearch.js build\n\n# Unit tests (Jest, jsdom)\nyarn jest <path-or-pattern>     # fastest loop: run one file/pattern at root\nyarn jest packages/<pkg>        # one package's suite (flavor pkgs have no `test` script — scope jest by path)\nyarn jest common-widgets        # shared cross-flavor widget suites (JS+React+Vue); -connectors for connectors\nyarn jest common-widgets -t \"Chat widget common tests\"   # scope to one widget (label = \"<Widget> widget common tests\")\nyarn test                       # everything (slow)\n\n# Lint (oxlint) + format (oxfmt) + types\nyarn lint:changed               # lint only changed files — use this while iterating\nyarn lint:fix                   # auto-fix\nyarn format                     # format the repo; yarn format:check only reports\nyarn type-check                 # tsc + per-package (also :v3 / :v4 for legacy algoliasearch)\n\n# E2E — see .claude/rules/e2e.md (Playwright). Examples must be built first:\nyarn website:examples && E2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n```\n\n## Conventions\n\n- **TypeScript strict.** No implicit any; unused vars/params error. Avoid `for-in`/`for-of` and `async` in library code (oxlint enforces).\n- **Tests** co-locate in `__tests__/` next to source — Jest picks up _any_ file under `__tests__/` (the default `testMatch`), so some legacy tests are plain `*.js`; name new ones `*.test.ts(x)`. Prefer focused assertions; use inline snapshots sparingly (initial render only). Mocks/helpers come from `@instantsearch/mocks` and `@instantsearch/testutils`.\n- **Cross-flavor tests** live in `tests/common/{widgets,connectors}/<name>/` and each flavor registers them in its `common-widgets.test.*` / `common-connectors.test.*`. Run with `yarn jest common-widgets` (all three flavors) and scope to one widget via `-t \"<Widget> widget common tests\"`. This is the primary test surface for newer widgets like Autocomplete and Chat. See `tests/common/README.md`.\n- **Commits: Conventional Commits** — `type(scope): description`, scope = widget/connector or topic (`deps`, `ci`). e.g. `fix(searchbox): increase magnifying glass size`, `feat(hits): add custom rendering`. Reference issues with `fix #1234` in the body.\n- **Branches:** target `master`; `vX` branches are critical-fix-only. Branch names: `fix/<issue>`, `feat/<name>`.\n- **Releases** are automated via Ship.js (`yarn release`); changelogs are generated from commits — don't hand-edit them.\n- **Agent workflows that write to another repo** (`.github/workflows/docs-*.yml`) must run with the working directory _inside_ that repo. A `CLAUDE.md`/`AGENTS.md` in a workspace subdirectory is never loaded, so the target repo's conventions get silently ignored — reach the source of this repo via `--add-dir` instead. Same for `--allowedTools`: `Bash(git diff:*)` matches, `Bash(git diff *)` matches nothing, and every denied call burns a turn.\n\n## Keep these docs alive\n\nIf you discover something durable and non-obvious while working — a gotcha that cost you a debugging detour, a convention, a non-obvious file location, a cross-flavor constraint — **propose** adding it to the right doc (package `CLAUDE.md` if package-specific, this file if repo-wide, `.claude/rules/e2e.md` for e2e). Surface the proposed edit for the user to approve rather than editing silently. Bar: it must generalize beyond the current task (same bar as a good code comment) and not already be documented.\n\n## Reference docs (read before relevant work — don't duplicate here)\n\n- `CONTRIBUTING.md` — full process, commit/branch rules, package-import flow.\n- `.claude/rules/e2e.md` — E2E testing (Playwright helpers, flavors, debugging).\n- `.claude/skills/port-widget/` — adding a widget across all three flavors (the `/port-widget` skill).\n- `tests/common/README.md` — shared cross-flavor test architecture.\n- Per-package `CLAUDE.md` in `instantsearch.js`, `instantsearch-ui-components`, `react-instantsearch`, `vue-instantsearch` for package-specific layout.\n","packages/algoliasearch-helper/CLAUDE.md":"# algoliasearch-helper\n\nThe **low-level search-state manager** that sits _underneath_ InstantSearch connectors. It's a separately-published, long-stable npm package (`algoliasearch-helper`, own semver — currently 3.x) that wraps the `algoliasearch` JS client with a higher-level, stateful API: it tracks search parameters, builds the actual Algolia queries, parses responses, and emits events. Every connector in `instantsearch.js` reads and writes this helper's state — but the connector is the layer you change, not this one.\n\n> **⚠️ Rarely the right place for a change/fix/update.** This package is mature and broadly depended on — InstantSearch (all flavors) plus other Algolia front-end libraries build on it. Almost all widget/connector behavior is implemented _on top of_ the helper, not in it. Touch it **only** for a genuine state-manager or response-parsing bug, or to add a real new search-parameter primitive. Any change here is a **public-API, independently-versioned** change with a blast radius far beyond this repo — flag it explicitly and prefer fixing the connector instead.\n\n## What's in it\n\nEntry: `algoliasearchHelper(client, index, opts)` → an `AlgoliaSearchHelper` (an `EventEmitter`). Core pieces under `src/`:\n\n- **`SearchParameters/`** — the **immutable** query state (query, `facets`/`disjunctiveFacets`/`hierarchicalFacets`, numeric & tag refinements, `page`, etc.). Setters return a _new_ instance; never mutate in place. This is the object connectors mutate via `getWidgetSearchParameters`.\n- **`SearchResults/`** — parses the raw Algolia response into a richer structure: facet values, `facets_stats`, and hierarchical facet trees (`generate-hierarchical-tree.js`).\n- **`requestBuilder.js`** — turns `SearchParameters` into the actual query payload(s); notably **splits disjunctive faceting into multiple queries**.\n- **`DerivedHelper/`** — sub-requests derived from a main helper (federated / multi-index search); this is what powers the `index` widget.\n- **`RecommendParameters/` + `RecommendResults/`** — the equivalent state/results pair for the Recommend API.\n- **`functions/`** — small internal lodash-style utilities (`merge`, `omit`, `orderBy`, …).\n\nEvents emitted by the helper (what InstantSearch's lifecycle listens to): `change`, `search`, `result`, `error`, `fetch`, `searchOnce`, `searchForFacetValues`, `searchQueueEmpty`, `recommendQueueEmpty`.\n\n## Conventions that differ from the rest of the monorepo\n\n- **Plain CommonJS JavaScript**, not the TS-strict source the other packages use. Public types are **hand-written** in `index.d.ts` — update them by hand if you change the surface.\n- Has its own **`README.md` + `CHANGELOG.md`** (don't fold into the monorepo's). Build is Rollup (`yarn workspace algoliasearch-helper build`); tests run via its own `scripts/test.sh` (`yarn workspace algoliasearch-helper test`).\n","packages/algoliasearch-helper/README.md":"_Coming from V1 (or js client v2)?_ Read the [migration guide](https://github.com/algolia/algoliasearch-helper-js/wiki/Migration-guide-:-V1-to-V2) to the new version of the Helper.\n\n_Coming from V2?_ Read the [migration guide](https://github.com/algolia/algoliasearch-helper-js/wiki/Migration-guide-:-V2-to-V3) to the new version of the Helper.\n\n**The JavaScript helper is an advanced library we provide to our users. If you are looking to build a complete search interface, we recommend you to use [InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/). If you want to build an autocomplete menu, see [Autocomplete](https://github.com/algolia/autocomplete).**\n\n# algoliasearch-helper\n\nThis module is the companion of the [algolia/algoliasearch-client-javascript](https://github.com/algolia/algoliasearch-client-javascript). It helps you keep track of the search parameters and provides a higher level API.\n\n[See the helper in action](https://community.algolia.com/algoliasearch-helper-js/)\n\n[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Downloads][downloads-image]][downloads-url] [![jsDelivr Hits][jsdelivr-badge]][jsdelivr-hits]\n\n[license-image]: http://img.shields.io/badge/license-MIT-green.svg?style=flat-square\n[license-url]: LICENSE\n[downloads-image]: https://img.shields.io/npm/dm/algoliasearch-helper.svg?style=flat-square\n[downloads-url]: http://npm-stat.com/charts.html?package=algoliasearch-helper\n[version-svg]: https://img.shields.io/npm/v/algoliasearch-helper.svg?style=flat-square\n[package-url]: https://npmjs.org/package/algoliasearch-helper\n[jsdelivr-badge]: https://data.jsdelivr.com/v1/package/npm/algoliasearch-helper/badge\n[jsdelivr-hits]: https://www.jsdelivr.com/package/npm/algoliasearch-helper\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Features](#features)\n- [Examples](#examples)\n  - [Vanilla JavaScript](#vanilla-javascript)\n  - [AngularJS module](#angularjs-module)\n- [Helper cheatsheet](#helper-cheatsheet)\n  - [Add the helper in your project](#add-the-helper-in-your-project)\n  - [Regular `<script>` tag](#regular-script-tag)\n  - [With npm](#with-npm)\n  - [Init the helper](#init-the-helper)\n  - [Helper lifecycle](#helper-lifecycle)\n  - [Objects](#objects)\n  - [Search](#search)\n  - [Events](#events)\n  - [Query](#query)\n  - [Filtering results](#filtering-results)\n  - [Facet utilities](#facet-utilities)\n  - [Tags](#tags)\n  - [Pagination](#pagination)\n  - [Index](#index)\n  - [One time query](#one-time-query)\n  - [Query parameters](#query-parameters)\n  - [Results format](#results-format)\n- [Browser support](#browser-support)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Features\n\n- Search parameters management\n- Facets exclusions\n- Pagination\n- Disjunctive faceting (search on two or more values of the same facet)\n\n## Examples\n\n### Vanilla JavaScript\n\nA small example that uses Browserify to manage modules.\n\n```js\nvar algoliasearch = require('algoliasearch');\nvar algoliasearchHelper = require('algoliasearch-helper');\n\nvar client = algoliasearch('appId', 'apiKey');\n\nvar helper = algoliasearchHelper(client, 'indexName', {\n  facets: ['mainCharacterFirstName', 'year'],\n  disjunctiveFacets: ['director'],\n});\n\nhelper.on('result', function (event) {\n  console.log(event.results);\n});\n\nhelper.addDisjunctiveFacetRefinement('director', 'Clint Eastwood');\nhelper.addDisjunctiveFacetRefinement('director', 'Sofia Coppola');\n\nhelper.addNumericRefinement('year', '=', 2003);\n\n// Search for any movie filmed in 2003 and directed by either C. Eastwood or S. Coppola\nhelper.search();\n```\n\nSee more examples in the [examples folder](examples/)\n\n### AngularJS module\n\n```js\n<script src=\"https://cdn.jsdelivr.net/algoliasearch/3/algoliasearch.angular.js\"></script>\n<script src=\"dist/algoliasearch.helper.min.js\"></script>\n\n<script type=\"text/javascript\">\nangular.module('searchApp', ['ngSanitize', 'algoliasearch'])\n.controller('searchController', ['$scope', '$sce', 'algolia', function($scope, $sce, algolia) {\n  var algolia = algolia.Client('applicationId', 'apiKey');\n  $scope.q = '';\n  $scope.content = null;\n  $scope.helper = algoliasearchHelper(algolia, 'indexName', {\n    facets: ['type', 'shipping'],\n    disjunctiveFacets: ['category', 'manufacturer'],\n    hitsPerPage: 5,\n  });\n  $scope.helper.on('result', function(event) {\n    $scope.$apply(function() {\n      $scope.content = event.results;\n    });\n  });\n  $scope.toggleRefine = function($event, facet, value) {\n    $event.preventDefault();\n    $scope.helper.toggleRefine(facet, value).search();\n  };\n  $scope.$watch('q', function(q) {\n    $scope.helper.setQuery(q).search();\n  });\n  $scope.helper.search();\n}]);\n</script>\n```\n\nYou can see the full [Angular example here](https://codepen.io/Algolia/pen/PzZobK)\n\n## Helper cheatsheet\n\n[There is also a complete JSDoc](https://community.algolia.com/algoliasearch-helper-js/reference.html)\n\n### Add the helper in your project\n\n### Regular `<script>` tag\n\nUse our [jsDelivr](http://www.jsdelivr.com/) build:\n\n`<script src=\"https://cdn.jsdelivr.net/algoliasearch.helper/2/algoliasearch.helper.min.js\"></script>`\n\n### With npm\n\n`npm install algoliasearch-helper`\n\n### Init the helper\n\n```js\nvar helper = algoliasearchHelper(client, 'indexName' /*, parameters*/);\n```\n\n### Helper lifecycle\n\n1. modify the parameters of the search (usually through user interactions)<br/> `helper.setQuery('iphone').addFacetRefinement('category', 'phone')`\n\n2. trigger the search (after all the modification have been applied)<br/> `helper.search()`\n\n3. read the results (with the event \"result\" handler) and update the UI with the results<br/> `helper.on('result', function(event) { updateUI(event.results); });`\n\n4. go back to 1\n\n### Objects\n\n**AlgoliasearchHelper**: the helper. Keeps the state of the search, makes the queries and calls the handlers when an event happen.\n\n**SearchParameters**: the object representing the state of the search. The current state is stored in `helperInstance.state`.\n\n**SearchResults**: the object in which the Algolia answers are transformed into. This object is passed to the result event handler. An example of SearchResults in JSON is available at [the end of this readme](#results-format)\n\n### Search\n\nThe search is triggered by the `search()` method.\n\nIt takes all the previous modifications to the search and uses them to create the queries to Algolia. The search parameters are immutable.\n\nExample:\n\n```js\nvar helper = algoliasearchHelper(client, indexName);\n\n// Let's monitor the results with the console\nhelper.on('result', function (event) {\n  console.log(event.results);\n});\n\n// Let's make an empty search\n// The results are all sorted using the dashboard configuration\nhelper.search();\n\n// Let's search for \"landscape\"\nhelper.setQuery('landscape').search();\n\n// Let's add a category \"photo\"\n// Will make a search with \"photo\" tag and \"landscape\" as the query\nhelper.addTag('photo').search();\n```\n\n### Events\n\nThe helper is a Node.js [EventEmitter](https://nodejs.org/api/events.html#events_class_events_eventemitter) instance.\n\n`result`: get notified when new results are received. The handler function will receive two objects (`SearchResults` and `SearchParameters`).\n\n`error`: get notified when errors are received from the API.\n\n`change`: get notified when a property has changed in the helper\n\n`search` : get notified when a request is sent to Algolia\n\n#### Listen to the `result` event\n\n```js\nhelper.on('result', updateTheResults);\n```\n\n#### Listen to a `result` event once\n\n```js\nhelper.once('result', updateTheResults);\n```\n\n#### Remove a `result` listener\n\n```js\nhelper.removeListener('result', updateTheResults);\n```\n\n#### Remove all `result` listeners\n\n```js\nhelper.removeAllListeners('result');\n```\n\nAll the methods from Node.js [EventEmitter](https://nodejs.org/api/events.html#events_class_events_eventemitter) class are available.\n\n### Query\n\n#### Do a search with the query \"fruit\"\n\n```javscript\nhelper.setQuery('fruit').search();\n```\n\n### Filtering results\n\nFacets are filters to retrieve a subset of an index having a specific value for a given attribute. First you need to define which attribute will be used as a facet in the dashboard: [https://www.algolia.com/explorer#?tab=display](https://www.algolia.com/explorer#?tab=display)\n\n#### Regular (conjunctive) facets\n\nRefinements are ANDed by default (Conjunctive selection).\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  facets: ['ANDFacet'],\n});\n```\n\n##### Add a facet filter\n\n```js\nhelper.addFacetRefinement('ANDFacet', 'valueOfANDFacet').search();\n```\n\n##### Remove a facet filter\n\n```js\nhelper.removeFacetRefinement('ANDFacet', 'valueOfANDFacet').search();\n```\n\n#### Disjunctive facets\n\nRefinements are ORed by default (Disjunctive selection).\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  disjunctiveFacets: ['ORFacet'],\n});\n```\n\n##### Add a facet filter\n\n```js\nhelper.addDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();\n```\n\n##### Remove a facet filter\n\n```js\nhelper.removeDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();\n```\n\n#### Negative facets\n\nFilter so that we do NOT get a given facet\n\n##### Facet definition (same as \"AND\" facet)\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  facets: ['ANDFacet'],\n}).search();\n```\n\n##### Exclude a value for a facet\n\n```js\nhelper.addFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');\n```\n\n##### Remove an exclude from the list of excluded values\n\n```js\nhelper.removeFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');\n```\n\n#### Numeric facets\n\nFilter over numeric attributes with math operations like `=`, `>`, `<`, `>=`, `<=`. Can be used for numbers and dates (if converted to timestamp)\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  disjunctiveFacets: ['numericAttribute'],\n});\n```\n\n##### Add numeric refinements\n\n```js\nhelper.addNumericRefinement('numericAttribute', '=', '3').search();\n// filter to only the results that match numericAttribute=3\nhelper.addNumericRefinement('numericAttribute', '=', '4').search();\n// filter to only the results that match numericAttribute=3 AND numericAttribute=4\n\n// On another numeric with no previous filter\nhelper\n  .addNumericRefinement('numericAttribute2', '=', ['42', '56', '37'])\n  .search();\n// filter to only the results that match numericAttribute2=42 OR numericAttribute2=56 OR numericAttribute2=37\n```\n\n##### Remove a numeric refinement\n\n```js\nhelper.removeNumericRefinement('numericAttribute', '=', '3').search();\n```\n\n##### Batch numeric filter removal\n\n```js\n// for the single operator = on numericAttribute\nhelper.removeNumericRefinement('numericAttribute', '=').search();\n// for all the refinements on numericAttribute\nhelper.removeNumericRefinement('numericAttribute').search();\n```\n\n#### Hierarchical facets\n\nHierarchical facets are useful to build such navigation menus:\n\n```txt\n| products\n  > fruits\n    > citrus\n    | strawberries\n    | peaches\n    | apples\n```\n\nHere, we refined the search this way:\n\n- click on fruits\n- click on citrus\n\n##### Usage\n\nTo build such menu, you need to use hierarchical faceting:\n\n```javascript\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n    },\n  ],\n});\n```\n\n**Requirements:** All the specified `attributes` must be defined in your Algolia settings as attributes for faceting.\n\nGiven your objects looks like this:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": \"fruits\",\n    \"lvl1\": \"fruits > citrus\"\n  }\n}\n```\n\nAnd you refine `products`:\n\n```js\nhelper.toggleFacetRefinement('products', 'fruits > citrus');\n```\n\nYou will get a hierarchical presentation of your facet values: a navigation menu of your facet values.\n\n```js\nhelper.on('result', function (event) {\n  console.log(event.results.hierarchicalFacets[0]);\n  // {\n  //   'name': 'products',\n  //   'count': null,\n  //   'isRefined': true,\n  //   'path': null,\n  //   'data': [{\n  //     'name': 'fruits',\n  //     'path': 'fruits',\n  //     'count': 1,\n  //     'isRefined': true,\n  //     'data': [{\n  //       'name': 'citrus',\n  //       'path': 'fruits > citrus',\n  //       'count': 1,\n  //       'isRefined': true,\n  //       'data': null\n  //     }]\n  //   }]\n  // }\n});\n```\n\nTo ease navigation, we always:\n\n- provide the root level categories\n- provide the current refinement sub categories (`fruits > citrus > *`: n + 1)\n- provide the parent refinement (`fruits > citrus` => `fruits`: n -1) categories\n- refine the search using the current hierarchical refinement\n\n##### Multiple values per level\n\nYour records can also share multiple categories between one another by using arrays inside your object:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": [\"fruits\", \"color\"],\n    \"lvl1\": [\"fruits > citrus\", \"color > orange\"]\n  }\n},\n{\n  \"objectID\": \"456\",\n  \"name\": \"grapefruit\",\n  \"categories\": {\n    \"lvl0\": [\"fruits\", \"color\", \"new\"],\n    \"lvl1\": [\"fruits > citrus\", \"color > yellow\", \"new > citrus\"]\n  }\n}\n```\n\n##### Specifying another separator\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      separator: '|',\n    },\n  ],\n});\n\nhelper.toggleFacetRefinement('products', 'fruits|citrus');\n```\n\nWould mean that your objects look like so:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": \"fruits\",\n    \"lvl1\": \"fruits|citrus\"\n  }\n}\n```\n\n##### Specifying a different sort order for values\n\nThe default sort for the hierarchical facet view is: `isRefined:desc (first show refined), name:asc (then sort by name)`.\n\nYou can specify a different sort order by using:\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      sortBy: ['count:desc', 'name:asc'], // first show the most common values, then sort by name\n    },\n  ],\n});\n```\n\nThe available sort tokens are:\n\n- count\n- isRefined\n- name\n- path\n\n##### Restrict results and hierarchical values to non-root level\n\nLet's say you have a lot of levels:\n\n```\n- fruits\n  - yellow\n    - citrus\n      - spicy\n```\n\nBut you only want to get the values starting at \"citrus\", you can use `rootPath`\n\nYou can specify an root path to filter the hierarchical values\n\n```\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [{\n    name: 'products',\n    attributes: ['categories.lvl0', 'categories.lvl1', 'categories.lvl2', 'categories.lvl3'],\n    rootPath: 'fruits > yellow > citrus'\n  }]\n});\n```\n\nHaving a rootPath will refine the results on it **automatically**.\n\n##### Hide parent level of current parent level\n\nBy default the hierarchical facet is going to return the child and parent facet values of the current refinement.\n\nIf you do not want to get the parent facet values you can set showParentLevel to false\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      showParentLevel: false,\n    },\n  ],\n});\n```\n\n##### Asking for the current breadcrumb\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      separator: '|',\n    },\n  ],\n});\n\nhelper.toggleFacetRefinement('products', 'fruits|citrus');\nvar breadcrumb = helper.getHierarchicalFacetBreadcrumb('products');\n\nconsole.log(breadcrumb);\n// ['fruits', 'citrus']\n\nconsole.log(breadcrumb.join(' | '));\n// 'fruits | citrus'\n```\n\n#### Clearing filters\n\n##### Clear all the refinements for all the refined attributes\n\n```js\nhelper.clearRefinements().search();\n```\n\n##### Clear all the refinements for a specific attribute\n\n```js\nhelper.clearRefinements('ANDFacet').search();\n```\n\n##### [ADVANCED] Clear only the exclusions on the \"ANDFacet\" attribute\n\n```js\nhelper\n  .clearRefinements(function (value, attribute, type) {\n    return type === 'exclude' && attribute === 'ANDFacet';\n  })\n  .search();\n```\n\n### Facet utilities\n\n#### Get the values of a facet with the default sort\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetValues('age');\n  // It will be ordered :\n  //  - refined facets first\n  //  - then ordered by number of occurence (bigger count -> higher in the list)\n  //  - then ordered by name (alphabetically)\n});\n```\n\n#### Get the values of a facet with a custom sort\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetValues('age', { sortBy: ['count:asc'] });\n  // It will be ordered by number of occurence (lower number => higher position)\n  // Elements that can be sorted : count, name, isRefined\n  // Type of sort : 'asc' for ascending order, 'desc' for descending order\n});\n```\n\n#### Get the facet stats\n\n_This only apply on numeric based facets/attributes._\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetStats('age');\n});\n```\n\n### Tags\n\nTags are an easy way to do filtering. They are based on a special attribute in the records named `_tags`, which can be a single string value or an array of strings.\n\n#### Add a tag filter for the value \"landscape\"\n\n```js\nhelper.addTag('landscape').search();\n```\n\n#### Remove a tag filter for the value \"landscape\"\n\n```js\nhelper.removeTag('landscape').search();\n```\n\n#### Clear all the tags filters\n\n```js\nhelper.clearTags().search();\n```\n\n### Pagination\n\n#### Get the current page\n\n```js\nhelper.getPage();\n```\n\n#### Change page\n\n```js\nhelper.setPage(3).search();\n```\n\n#### Automatic reset to page 0\n\nDuring a search, changing the parameters will update the result set, which can then change the number of pages in the result set. Therefore, the behavior has been standardized so that any operation that may change the number of page will reset the pagination to page 0.\n\nThis may lead to some unexpected behavior. For example:\n\n```js\nhelper.setPage(4);\nhelper.getPage(); // 4\nhelper.setQuery('foo');\nhelper.getPage(); // 0\n```\n\nNon exhaustive list of operations that trigger a reset:\n\n- refinements (conjunctive, exclude, disjunctive, hierarchical, numeric)\n- tags\n- index (setIndex)\n- setQuery\n- setHitsPerPage\n- setTypoTolerance\n\n### Index\n\nIndex can be changed. The common use case is when you have several slaves with different sort order (sort by relevance, price or any other attribute).\n\n#### Change the current index\n\n```js\nhelper.setIndex('index_orderByPrice').search();\n```\n\n#### Get the current index\n\n```js\nvar currentIndex = helper.getIndex();\n```\n\n### One time query\n\nSometime it's convenient to reuse the current search parameters with small changes without changing the state stored in the helper. That's why there is a function called `searchOnce`. This method does not trigger `change` or `error` events.\n\nIn the following, we are using `searchOnce` to fetch only a single element using all the other parameters already set in the search parameters.\n\n#### Using searchOnce with a callback\n\n```js\nvar state = helper.searchOnce(\n  { hitsPerPage: 1 },\n  function (error, content, state) {\n    // if an error occured it will be passed in error, otherwise its value is null\n    // content contains the results formatted as a SearchResults\n    // state is the instance of SearchParameters used for this search\n  }\n);\n```\n\n#### Using searchOnce with a promise\n\n```js\nvar state1 = helper.searchOnce({ hitsPerPage: 1 }).then(function (res) {\n  // res contains\n  // {\n  //   content : SearchResults\n  //   state : SearchParameters (the one used for this specific search)\n  // }\n});\n```\n\n### Query parameters\n\nThere are lots of other parameters you can set.\n\n#### Set a parameter at the initialization of the helper\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hitsPerPage: 50,\n});\n```\n\n#### Set a parameter later\n\n```js\nhelper.setQueryParameter('hitsPerPage', 20).search();\n```\n\n#### List of parameters that can be set\n\n<table cellspacing=\"0\" cellpadding=\"0\" class=\"params\">\n  <tbody>\n    <tr>\n      <td valign=\"top\" class=\"td1\">\n        <p class=\"p1\"><span class=\"s1\"><b>Name</b></span></p>\n      </td>\n      <td valign=\"top\" class=\"td2\">\n        <p class=\"p1\"><span class=\"s1\"><b>Type</b></span></p>\n      </td>\n      <td valign=\"top\" class=\"td3\">\n        <p class=\"p1\"><span class=\"s1\"><b>Description</b></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">advancedSyntax</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the advanced syntax.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/advancedSyntax/\">advancedSyntax on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">allowTyposOnNumericTokens</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the engine allow typos on numerics.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/allowTyposOnNumericTokens/\">allowTyposOnNumericTokens on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">analytics</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the analytics</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/analytics/\">analytics on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">analyticsTags</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Tag of the query in the analytics.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/analyticsTags/\">analyticsTags on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundLatLng</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Center of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLng/\">aroundLatLng on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundLatLngViaIP</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Center of the search, retrieve from the user IP.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLngViaIP/\">aroundLatLngViaIP on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundPrecision</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Precision of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundPrecision/\">aroundPrecision on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundRadius</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Radius of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundRadius/\">aroundRadius on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minimumAroundRadius</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Minimum radius of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minimumAroundRadius/\">minimumAroundRadius on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToHighlight</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to highlight</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToHighlight/\">attributesToHighlight on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToRetrieve</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to retrieve</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToRetrieve/\">attributesToRetrieve on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToSnippet</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to snippet</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToSnippet/\">attributesToSnippet on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">disjunctiveFacets</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">Array.&lt;string&gt;</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">All the declared disjunctive facets</span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">distinct</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean|number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Remove duplicates based on the index setting attributeForDistinct</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/distinct/\">distinct on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">facets</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">Array.&lt;string&gt;</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">All the facets that will be requested to the server</span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">filters</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">Add filters to the query (similar to WHERE clauses)</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/filters/\">filters on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">getRankingInfo</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">integer</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the ranking informations in the response</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/getRankingInfo/\">getRankingInfo on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">hitsPerPage</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of hits to be returned by the search API</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/hitsPerPage/\">hitsPerPage on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">ignorePlurals</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the plurals be ignored</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/ignorePlurals/\">ignorePlurals on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">insideBoundingBox</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Geo search inside a box.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/insideBoundingBox/\">insideBoundingBox on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">insidePolygon</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Geo search inside a polygon.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/insidePolygon/\">insidePolygon on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">maxValuesPerFacet</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of values for each facetted attribute</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/maxValuesPerFacet/\">maxValuesPerFacet on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minWordSizefor1Typo</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of characters to wait before doing one character replacement.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minWordSizefor1Typo/\">minWordSizefor1Typo on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minWordSizefor2Typos</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of characters to wait before doing a second character replacement.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minWordSizefor2Typos/\">minWordSizefor2Typos on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">optionalWords</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Add some optional words to those defined in the dashboard</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/optionalWords/\">optionalWords on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">page</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">The current page number</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"algolia.com/doc/api-reference/api-parameters/page/\">page on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">query</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Query string of the instant search. The empty string is a valid query.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/query/\">query on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">queryType</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">How the query should be treated by the search engine. Possible values: prefixAll, prefixLast, prefixNone</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/queryType/\">queryType on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">removeWordsIfNoResults</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Possible values are \"lastWords\" \"firstWords\" \"allOptional\" \"none\" (default)</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/removeWordsIfNoResults/\">removeWordsIfNoResults on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">replaceSynonymsInHighlight</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the engine replace the synonyms in the highlighted results.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/replaceSynonymsInHighlight/\">replaceSynonymsInHighlight on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">restrictSearchableAttributes</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Restrict which attribute is searched.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/restrictSearchableAttributes/\">restrictSearchableAttributes on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">synonyms</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the synonyms</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/synonyms/\">synonyms on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">tagFilters</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Contains the tag filters in the raw format of the Algolia API. Setting this parameter is not compatible with the of the add/remove/toggle methods of the tag api.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/tagFilters/\">tagFilters on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">typoTolerance</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">How the typo tolerance behave in the search engine. Possible values: true, false, min, strict</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/typoTolerance/\">typoTolerance on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n  </tbody>\n</table>\n\n### Results format\n\nHere is an example of a result object you get with the `result` event.\n\n```js\n{\n   \"hitsPerPage\": 10,\n   \"processingTimeMS\": 2,\n   \"facets\": [\n      {\n         \"name\": \"type\",\n         \"data\": {\n            \"HardGood\": 6627,\n            \"BlackTie\": 550,\n            \"Music\": 665,\n            \"Software\": 131,\n            \"Game\": 456,\n            \"Movie\": 1571\n         },\n         \"exhaustive\": false\n      },\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"Free shipping\": 5507\n         },\n         \"name\": \"shipping\"\n      }\n   ],\n   \"hits\": [\n      {\n         \"thumbnailImage\": \"http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_54x108_s.gif\",\n         \"_highlightResult\": {\n            \"shortDescription\": {\n               \"matchLevel\": \"none\",\n               \"value\": \"Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection\",\n               \"matchedWords\": []\n            },\n            \"category\": {\n               \"matchLevel\": \"none\",\n               \"value\": \"Computer Security Software\",\n               \"matchedWords\": []\n            },\n            \"manufacturer\": {\n               \"matchedWords\": [],\n               \"value\": \"Webroot\",\n               \"matchLevel\": \"none\"\n            },\n            \"name\": {\n               \"value\": \"Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows\",\n               \"matchedWords\": [],\n               \"matchLevel\": \"none\"\n            }\n         },\n         \"image\": \"http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_105x210_sc.jpg\",\n         \"shipping\": \"Free shipping\",\n         \"bestSellingRank\": 4,\n         \"shortDescription\": \"Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection\",\n         \"url\": \"http://www.bestbuy.com/site/webroot-secureanywhere-internet-security-3-devi…d=1219060687969&skuId=1688832&cmp=RMX&ky=2d3GfEmNIzjA0vkzveHdZEBgpPCyMnLTJ\",\n         \"name\": \"Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows\",\n         \"category\": \"Computer Security Software\",\n         \"salePrice_range\": \"1 - 50\",\n         \"objectID\": \"1688832\",\n         \"type\": \"Software\",\n         \"customerReviewCount\": 5980,\n         \"salePrice\": 49.99,\n         \"manufacturer\": \"Webroot\"\n      },\n      ....\n   ],\n   \"nbHits\": 10000,\n   \"disjunctiveFacets\": [\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"5\": 183,\n            \"12\": 112,\n            \"7\": 149,\n            ...\n         },\n         \"name\": \"customerReviewCount\",\n         \"stats\": {\n            \"max\": 7461,\n            \"avg\": 157.939,\n            \"min\": 1\n         }\n      },\n      {\n         \"data\": {\n            \"Printer Ink\": 142,\n            \"Wireless Speakers\": 60,\n            \"Point & Shoot Cameras\": 48,\n            ...\n         },\n         \"name\": \"category\",\n         \"exhaustive\": false\n      },\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"> 5000\": 2,\n            \"1 - 50\": 6524,\n            \"501 - 2000\": 566,\n            \"201 - 500\": 1501,\n            \"101 - 200\": 1360,\n            \"2001 - 5000\": 47\n         },\n         \"name\": \"salePrice_range\"\n      },\n      {\n         \"data\": {\n            \"Dynex™\": 202,\n            \"Insignia™\": 230,\n            \"PNY\": 72,\n            ...\n         },\n         \"name\": \"manufacturer\",\n         \"exhaustive\": false\n      }\n   ],\n   \"query\": \"\",\n   \"nbPages\": 100,\n   \"page\": 0,\n   \"index\": \"bestbuy\"\n}\n```\n\n## Browser support\n\nThis project works on any [ES5](https://en.wikipedia.org/wiki/ECMAScript#5th_Edition) browser, basically >= IE9+.\n","packages/algoliasearch-helper/examples/readme.md":"The examples moved to [codepen](http://codepen.io/collection/nwWJbe/).\n","packages/create-instantsearch-app/README.md":"# Create InstantSearch App\n\n> ⚡️ Build InstantSearch apps at the speed of thought.\n\n[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Build Status][ci-svg]][ci-url]\n\n`create-instantsearch-app` is a command line utility that helps you quick start your InstantSearch app using any [Algolia][algolia-website] InstantSearch flavor ([InstantSearch.js][instantsearchjs-github], [React InstantSearch][react-instantsearch-github], [Vue InstantSearch][vue-instantsearch-github], [InstantSearch iOS][instantsearch-ios-github] and [InstantSearch Android][instantsearch-android-github]).\n\n<p align=\"center\">\n  <img src=\"preview.png\" width=\"800\" alt=\"Preview\">\n</p>\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Get started](#get-started)\n- [Usage](#usage)\n- [API](#api)\n- [Tutorials](#tutorials)\n- [Previews](#previews)\n- [Contributing](#contributing)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## Get started\n\n> The tool requires Node ≥ 8.\n\n```\nnpx create-instantsearch-app my-app\ncd my-app\nnpm start\n```\n\n> [`npx`](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) is a tool introduced in `npm@5.2.0` that makes it possible to run CLI tools hosted on the npm registry.\n\n---\n\nAlternatively, you can use [Yarn](https://yarnpkg.com):\n\n```\nyarn create instantsearch-app my-app\ncd my-app\nyarn start\n```\n\n> **Important** Selecting 'Y' when being prompted to ‘Enable user events’ activates the [`insights`](https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/#widget-param-insights) option for compatible templates, allowing Algolia to process your user Events. Events can unlock powerful features, enhancing your application's effectiveness and we encourage you to consider enabling this valuable functionality. Please review our [API reference](https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/#widget-param-insights) for more details about Events collection and settings.\n\n## Usage\n\nThis package comes with the module `createInstantSearchApp(path, options?)` and the command-line tool `create-instantsearch-app`.\n\n```\n$ create-instantsearch-app --help\n\n  Usage: create-instantsearch-app <project-directory> [options]\n\n  Options:\n\n    -v, --version                                      output the version number\n    --name <name>                                      The name of the application\n    --app-id <appId>                                   The application ID\n    --api-key <apiKey>                                 The Algolia search API key\n    --index-name <indexName>                           The main index of your search\n    --attributes-to-display <attributesToDisplay>      The attributes of your index to display\n    --image-attribute <imageAttribute>                 The attribute of your index to use for image display\n    --attributes-for-faceting <attributesForFaceting>  The attributes for faceting\n    --template <template>                              The InstantSearch template to use\n    --library-version <libraryVersion>                 The version of the library\n    --config <config>                                  The configuration file to get the options from\n    --no-installation                                  Ignore dependency installation\n    --no-interactive                                   Do not ask any interactive questions\n    -h, --help                                         output usage information\n```\n\n#### `--template`\n\nSupported templates are:\n\n- [`InstantSearch.js`][instantsearchjs-github]\n- [`React InstantSearch`][react-instantsearch-github]\n- [`React InstantSearch Native`][react-instantsearch-github]\n- [`Vue InstantSearch`][vue-instantsearch-github]\n- [`InstantSearch iOS`][instantsearch-ios-github]\n- [`InstantSearch Android`][instantsearch-android-github]\n\n```\ncreate-instantsearch-app my-app --template \"React InstantSearch\"\n```\n\nYou can also [create your own template](docs/custom-templates.md) and specify its path.\n\n#### `--config`\n\nThe `config` flag is handy to automate app generations.\n\n<h6 align=\"center\">config.json</h6>\n\n```json\n{\n  \"name\": \"my-app\",\n  \"template\": \"InstantSearch.js\",\n  \"libraryVersion\": \"2.8.0\",\n  \"appId\": \"MY_APP_ID\",\n  \"apiKey\": \"MY_API_KEY\",\n  \"indexName\": \"MY_INDEX_NAME\",\n  \"searchPlaceholder\": \"Search\",\n  \"attributesToDisplay\": [\"name\", \"description\"],\n  \"imageAttribute\": \"image\",\n  \"attributesForFaceting\": [\"brand\", \"location\"],\n  \"enableInsights\": true\n}\n```\n\nCreate the app based on this configuration:\n\n```\ncreate-instantsearch-app my-app --config config.json\n```\n\n## API\n\n`create-instantsearch-app` is based on the module `createInstantSearchApp(path, options?)`. The same [camel cased](https://en.wikipedia.org/wiki/Camel_case) options as the CLI are available.\n\n```javascript\nconst createInstantSearchApp = require('create-instantsearch-app');\n\nconst app = createInstantSearchApp('~/lab/my-app', {\n  template: 'InstantSearch.js',\n  libraryVersion: '2.0.0',\n  attributesToDisplay: ['name', 'description'],\n  imageAttribute: 'image',\n  attributesForFaceting: ['keywords'],\n  enableInsights: true,\n});\n\napp.create().then(() => console.log('App generated!'));\n```\n\n## Tutorials\n\n- [Creating a custom template](docs/custom-templates.md)\n- [Deploying an InstantSearch App](docs/deploy.md)\n\n## Previews\n\nYou can use the web templates on CodeSandbox:\n\n- [InstantSearch.js](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/instantsearch.js)\n- [React InstantSearch](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/react-instantsearch)\n- [Vue InstantSearch](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/vue-instantsearch)\n- [Autocomplete.js](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/autocomplete.js)\n- [JavaScript Client](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/javascript-client)\n- [JavaScript Helper](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/javascript-helper)\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to the code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nCreate InstantSearch App is [MIT licensed](LICENSE).\n\n<!-- Badges -->\n\n[version-svg]: https://img.shields.io/npm/v/create-instantsearch-app.svg?style=flat-square\n[package-url]: https://npmjs.org/package/create-instantsearch-app\n[ci-svg]: https://img.shields.io/circleci/project/github/algolia/create-instantsearch-app.svg?style=flat-square\n[ci-url]: https://circleci.com/gh/algolia/instantsearch\n[license-image]: https://img.shields.io/badge/license-MIT-green.svg?style=flat-square\n[license-url]: LICENSE\n\n<!-- Links -->\n\n[algolia-website]: https://www.algolia.com/?utm_medium=social-owned&utm_source=GitHub&utm_campaign=create-instantsearch-app%20repository\n[instantsearchjs-github]: https://github.com/algolia/instantsearch/tree/master/packages/instantsearch.js\n[react-instantsearch-github]: https://github.com/algolia/instantsearch/tree/master/packages/react-instantsearch\n[vue-instantsearch-github]: https://github.com/algolia/instantsearch/tree/master/packages/vue-instantsearch\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20Create+InstantSearch+App\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20Create+InstantSearch+App&title=Feature%20request%3A%20\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n","packages/create-instantsearch-app/docs/custom-templates.md":"# Creating a custom template\n\nBesides all the InstantSearch templates provided, the Create InstantSearch API enables you to create your own template.\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Defining the template](#defining-the-template)\n  - [`libraryName`](#libraryname)\n  - [`tasks`](#tasks)\n- [Injecting values to the template](#injecting-values-to-the-template)\n- [Using the template](#using-the-template)\n- [Examples](#examples)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## Defining the template\n\nOnce you've created a project directory, you can turn it into a template by adding a file `.template.js` at its root. This is the configuration file that drives `createInstantSearchApp()` to bootstrap your application.\n\nThis definition file returns a object with several properties.\n\n<h6 align=\"center\">.template.js</h6>\n\n```javascript\nconst { execSync } = require('child_process');\n\nmodule.exports = {\n  libraryName: 'react-instantsearch',\n  tasks: {\n    install(config) {\n      execSync(`cd ${config.path} && npm install`);\n    },\n    teardown(config) {\n      console.log('Begin by running: npm start');\n    },\n  },\n};\n```\n\n### `libraryName`\n\n> `string` (optional) | no default\n\nThe name of the [npm](https://www.npmjs.com) library to fetch versions for.\n\n### `tasks`\n\n> `object` | defaults as follows\n\nThe tasks are hooks that define your [application generation lifecycle](#lifecycle). Each of these hooks are functions which receives the [app configuration](#configuration-object) as parameters. You can use this configuration to drive the application generation.\n\n#### `setup(config: object)`\n\n> No default\n\nCan be leveraged to check the project requirements.\n\n#### `build(config: object)`\n\n> Defaults to copying and injecting the values\n\nCan be leveraged to generate the project differently.\n\n#### `install(config: object)`\n\n> No default\n\nCan be leveraged to install dependencies with a package manager.\n\n#### `clean(config: object)`\n\n> Defaults to deleting the project which installation has failed\n\nCan be leveraged to warn the user that the installation has failed.\n\n#### `teardown(config: object)`\n\n> No default\n\nCan be leveraged to display instructions to run the app.\n\n<h6 align=\"center\" id=\"lifecycle\">Application generation lifeycle</h6>\n\n![Setup → Build → Install → Clean (if installation fails) → Teardown](https://user-images.githubusercontent.com/6137112/41421858-f838c2a6-6ff7-11e8-8cef-4cc07f1f4f44.png)\n\n<h6 align=\"center\" id=\"configuration-object\"><code>config</code> object</h6>\n\n```js\n{\n  name: 'app-name',\n  path: '/dev/app-name',\n  template: 'InstantSearch.js',\n  libraryVersion: '2.9.0',\n  appId: 'APP_ID',\n  apiKey: 'API_KEY',\n  indexName: 'INDEX_NAME',\n  attributesToDisplay: ['title', 'description'],\n  installation: true,\n  silent: false,\n}\n```\n\n## Injecting values to the template\n\nBy default, the `build` task supports the [Handlebars](https://handlebarsjs.com) syntax to inject values passed to Create InstantSearch App. You can explicitly suffix the template files with `.hbs` or leave them as is (`.js` in this case).\n\n<h6 align=\"center\">App.js.hbs</h6>\n\n```handlebars\nimport React, { Component } from 'react';\nimport algoliasearch from 'algoliasearch/lite';\nimport {\n  InstantSearch,\n  Hits,\n  SearchBox,\n  {{#if attributesToDisplay}}\n  Highlight,\n  {{/if}}\n} from 'react-instantsearch-dom';\n\nconst searchClient = algoliasearch(\n  '{{appId}}',\n  '{{apiKey}}'\n);\n\n<InstantSearch searchClient={searchClient} indexName=\"{{indexName}}\">\n  <SearchBox placeholder=\"{{searchPlaceholder}}\" />\n  {/* ... */}\n</InstantSearch>\n```\n\n## Using the template\n\nCreate InstantSearch App reads your `.template.js` file and gets the specified tasks in the provided template.\n\nUsing the API:\n\n```javascript\nconst app = createInstantSearchApp('my-app', {\n  template: '/path/to/my-custom-template',\n});\n\napp.create();\n```\n\nUsing the CLI:\n\n```\ncreate-instantsearch-app my-app --template /path/to/my-custom-template\n```\n\n## Examples\n\nYou can find all the officially supported templates in the [`src/templates/`](../src/templates) folder.\n","packages/create-instantsearch-app/docs/deploy.md":"# Deploy an InstantSearch app\n\nCreate InstantSearch App generates applications that are ready to be deployed. They rely on bundlers and framework toolkits (Parcel, Create React App, Vue CLI, etc.).\n\nYour app can be deployed in a single command, assuming you're located in its folder.\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [CodeSandbox](#codesandbox)\n- [Surge](#surge)\n- [Vercel](#vercel)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## CodeSandbox\n\n```\nnpx codesandbox .\n```\n\n[Learn more about CodeSandbox →](https://codesandbox.io)\n\n## Surge\n\n```\nnpx surge\n```\n\n[Learn more about Surge →](https://surge.sh)\n\n## Vercel\n\n```\nnpx vercel\n```\n\n[Learn more about Vercel →](https://vercel.com)\n","packages/create-instantsearch-app/src/templates/Autocomplete/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/create-instantsearch-app/src/templates/InstantSearch.js widget/README.md":"# {{ name }}\n\n{{ description }}\n\n---\n\n[![MIT](https://img.shields.io/npm/l/{{ packageName }})](./LICENSE) [![NPM version](http://img.shields.io/npm/v/{{ packageName }}.svg)](https://npmjs.org/package/{{ packageName }})\n\n## Install\n\n```bash\nnpm install {{ packageName }}\n# or\nyarn add {{ packageName }}\n```\n\n## Widget\n\n### Usage\n\n```js\nimport instantsearch from 'instantsearch.js';\nimport algoliasearch from 'algoliasearch/lite';\nimport { {{ camelCaseName }} } from '{{ packageName }}';\n\nconst searchClient = algoliasearch('appId', 'apiKey');\n\nconst search = instantsearch({\n  indexName: 'indexName',\n  searchClient,\n});\n\nsearch.addWidgets([\n  {{ camelCaseName }}({\n    // widget parameters\n  }),\n]);\n\nsearch.start();\n```\n\n### Options\n\n| Option | Type | Required | Default | Description |\n| :-- | :-- | :-- | :-- | --- |\n| [`container`](#container) | `string` or `HTMLElement` | true | - | The element to insert the widget into. |\n| [`option1`](#option1) | `...` | true | - | REPLACE WITH THE DESCRIPTION FOR THIS OPTION |\n\n#### container\n\n> `string | Element` | **required**\n\nThe element to insert the widget into.\n\nThis can be either a valid CSS Selector:\n\n```js\n{{ camelCaseName }}({\n  container: '#{{ name }}',\n  // ...\n});\n```\n\nor an `HTMLElement`:\n\n```js\n{{ camelCaseName }}({\n  container: document.querySelector('#{{ name }}'),\n  // ...\n});\n```\n\n#### option1\n\n> `...` | **required**\n\nREPLACE WITH THE DESCRIPTION FOR THIS OPTION\n\n```js\n{{ camelCaseName }}({\n  option1: 'value',\n  // ...\n});\n```\n\n## Connector\n\n### Usage\n\n```js\nimport { connect{{ pascalCaseName }} } from '{{ packageName }}';\n\n// 1. Create a render function\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  // Rendering logic\n};\n\n// 2. Create the custom widget\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }}\n);\n\n// 3. Instantiate\nsearch.addWidgets([\n  custom{{ pascalCaseName }}({\n    // instance params\n  }),\n]);\n```\n\n### Options\n\n#### option1\n\n> `...`\n\nREPLACE WITH THE DESCRIPTION FOR THIS RENDERING ITEM\n\n```js\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  // show how to use this render option\n};\n\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }},\n);\n\nsearch.addWidgets([\n  custom{{pascalCaseName }}({\n    // ...\n  }),\n]);\n```\n\n#### widgetParams\n\n> `object`\n\nAll original widget options forwarded to the render function.\n\n```js\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  const { widgetParams } = renderOptions;\n  widgetParams.container.innerHTML = '...';\n};\n\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }},\n);\n\nsearch.addWidgets([\n  custom{{ pascalCaseName }}({\n    container: document.querySelector('#{{ name }}'),\n    // ...\n  }),\n]);\n```\n\n## Contributing\n\nTo start contributing to code, you need to:\n\n1. [Fork the project](https://docs.github.com/en/get-started/quickstart/fork-a-repo)\n2. [Clone the repository](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository)\n3. Install the dependencies: `yarn`\n4. Run the development mode: `yarn start`\n\nPlease read [our contribution process](./CONTRIBUTING.md) to learn more.\n\n---\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n","packages/create-instantsearch-app/src/templates/InstantSearch.js/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/create-instantsearch-app/src/templates/JavaScript Client/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/create-instantsearch-app/src/templates/JavaScript Helper/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/create-instantsearch-app/src/templates/React InstantSearch Native/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n","packages/create-instantsearch-app/src/templates/React InstantSearch/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n","packages/create-instantsearch-app/src/templates/Vue InstantSearch with Vue 3/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/create-instantsearch-app/src/templates/Vue InstantSearch/README.md":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","packages/instantsearch-codemods/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Codemods](#codemods)\n  - [`rish-to-ris`](#rish-to-ris)\n  - [`ris-v6-to-v7`](#ris-v6-to-v7)\n  - [`addWidget-to-addWidgets`](#addwidget-to-addwidgets)\n  - [Notes](#notes)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Codemods\n\nThese codemods (code transformers) can be ran with [jscodeshift](https://github.com/facebook/jscodeshift).\n\n### `rish-to-ris`\n\nThis will replace all imports from `react-instantsearch-hooks` to their `react-instantsearch` equivalent.\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#rish-to-ris' <path>\n```\n\n### `ris-v6-to-v7`\n\nThis will:\n\n- Replace `react-instantsearch-dom` imports to `react-instantsearch`\n- Replace prop names to their new equivalent\n- Replace `translations` keys to their new equivalent if they are defined inline, otherwise it will add a TODO comment\n- Add some TODO comments to help you migrate\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#ris-v6-to-v7' <path>\n```\n\n### `addWidget-to-addWidgets`\n\nThis will replace all `addWidget` calls to `addWidgets` and `removeWidget` calls to `removeWidgets`.\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#addWidget-to-addWidgets' <path>\n```\n\n### Notes\n\nIf you are using a formatter or a linter, make sure to run its autofixing after this transformation, since code can be formatted differently after it has been transformed. In our repository, that would be:\n\n```\nyarn format\n```\n","packages/instantsearch-ui-components/CLAUDE.md":"# instantsearch-ui-components\n\nThe **framework-agnostic UI layer** — the shared markup/layout for widgets, reused by every flavor. This is where a widget's DOM structure and `ais-*` CSS classes live for **newer** widgets. (Older widgets still define their layout per-flavor; see \"Old vs new\" below.)\n\n## The factory + renderer pattern\n\nComponents are **not** React/Preact/Vue components directly. Each is a factory that takes a renderer and returns a component:\n\n```tsx\n/** @jsx createElement */\nimport { cx } from '../lib';\nimport type { Renderer, ComponentProps } from '../types';\n\nexport function createHitsComponent({ createElement, Fragment }: Renderer) {\n  return function Hits(props) {\n    return <div className={cx('ais-Hits', props.classNames.root)}>…</div>;\n  };\n}\n```\n\n- **`Renderer` = `{ createElement, Fragment }`** is _injected_ by the consumer (`src/types/Renderer.ts`). Defaults are Preact's, but React passes `react`'s `createElement` and Vue passes its own `h` (wired up via the `renderCompat` helper in `vue-instantsearch`). **Never import `preact`/`react`/`vue` directly** — go through the injected renderer. The `/** @jsx createElement */` pragma at the top of each file is what wires JSX to the injected function.\n- Class names go through **`cx`** (`src/lib/cx.ts`) and follow the `ais-<Widget>-<element>` contract that `instantsearch.css` themes — treat those class names as a public API.\n- Props are typed with `ComponentProps<'div'>`-style helpers from `src/types`; expose a `classNames` partial so consumers can extend styling.\n\n## Layout\n\n- `src/components/<Name>.tsx` — a `create<Name>Component` factory. Exported from `src/components/index.ts` → `src/index.ts`.\n- Grouped families: `src/components/autocomplete/`, `src/components/chat/`, `src/components/recommend-shared/` (+ the recommend widgets `RelatedProducts`, `FrequentlyBoughtTogether`, `LookingSimilar`, `TrendingItems`, `TrendingFacets`).\n- `src/lib/` — `cx`, `stickToBottom`, shared `utils`. `src/types/` — `Renderer`, `ComponentProps`, `Hooks`, `Recommend`, `shared`.\n- Runtime deps are minimal on purpose (`@swc/helpers`); **no framework as a dependency**. Markdown rendering uses a vendored copy of `markdown-to-jsx` in `src/lib/markdown-to-jsx/` (React removed, see its README); it is linted, formatted and type-checked like the rest.\n\n## Old vs new (important)\n\n- **New widgets:** layout lives here and is consumed by all flavors — React via `import { create<Name>Component } from 'instantsearch-ui-components'`, Vue via the `renderCompat` helper, JS via the Preact renderer.\n- **Older widgets:** layout was defined per-flavor — JS in `packages/instantsearch.js/src/components/` (Preact), React in `packages/react-instantsearch/src/ui/`. The migration direction is to consolidate that markup here; some JS-package components are now thin wrappers that import from this package.\n- When adding/changing **shared** layout, do it here. When touching a widget that still defines its own markup, check whether it should be migrated rather than forked.\n\n## Working here\n\n```bash\nyarn workspace instantsearch-ui-components build       # tsc/rollup + build:types\nyarn jest packages/instantsearch-ui-components/src/components/__tests__/<Name>   # from repo root\n```\n\n- Tests are co-located in `src/components/__tests__/`. Test the component by passing a concrete renderer (e.g. Preact's) — assert rendered structure and class names, not implementation.\n- A markup or class-name change here **ripples to React, Vue, and JS at once** — verify consumers and keep the `ais-*` contract stable (a class rename is a breaking change that also touches `instantsearch.css`).\n","packages/instantsearch-ui-components/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [instantsearch-ui-components](#instantsearch-ui-components)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# instantsearch-ui-components\n\nInstantSearch UI Components is an open-source UI library used by InstantSearch to build UI for search and discovery interfaces. It exports UI components that are compatible with InstantSearch.js, React InstantSearch, and Vue InstantSearch.\n\n> Note: `instantsearch-ui-components` exists for internal usage and isn't designed for public consumption.\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nInstantSearch UI Components is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","packages/instantsearch-ui-components/src/lib/markdown-to-jsx/README.md":"Vendored copy of [`markdown-to-jsx`](https://github.com/quantizor/markdown-to-jsx) v7.7.15 (MIT, see `LICENSE`), taken from the original TypeScript embedded in the npm package's source map.\n\nChanges from upstream:\n\n- React types are replaced by the shared `Renderer` types, so the module has no `react` import, not even as a type.\n- `createElement` must be passed in options (no `React.createElement` fallback).\n- The `<Markdown>` React component and the default export are removed.\n- Raw HTML parsing is removed: the `htmlBlock` and `htmlSelfClosing` rules, the `disableParsingRawHTML` option and the state, types and helpers that only they used. HTML comments are still dropped.\n- Formatted and linted with the repo's config, and type-checked under its strict TypeScript config: non-null assertions, `any` casts for dynamic indexing, `null` added to a few declared types, unused parameters prefixed with `_`, `var` replaced by `let`/`const`, a sparse array literal written with an explicit `undefined`, `LOOKAHEAD` renamed to `lookahead`, type parameters renamed to the `T` prefix convention, and `eslint-disable` comments for the dev-only `console.warn` calls, the namespace and a deprecated type. The emitted JavaScript is otherwise unchanged.\n","packages/instantsearch.css/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Getting started](#getting-started)\n- [Installation](#installation)\n- [Available themes](#available-themes)\n- [Browser support](#browser-support)\n- [Implemented versions](#implemented-versions)\n- [Contributing](#contributing)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n<p align=\"center\">\n  <p align=\"center\">\n    InstantSearch.css is a CSS library to style InstantSearch-powered search experiences.\n  </p>\n</p>\n\n---\n\n[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Build Status][ci-svg]][ci-url] [![Website][website-svg]][website-url]\n\n## Getting started\n\nUsing InstantSearch.css is as simple as adding this CSS code to your existing InstantSearch app:\n\n```js\n// Include only the reset\nimport 'instantsearch.css/themes/reset.css';\n\n// Or include the full Satellite theme\nimport 'instantsearch.css/themes/satellite.css';\n```\n\nTo learn more about the library, follow the guide on how to style your InstantSearch widgets:\n\n- [InstantSearch.js](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/js/#style-your-widgets)\n- [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/react/#style-your-widgets)\n- [Vue InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/vue/#style-your-widgets)\n\n## Installation\n\n```sh\nnpm install instantsearch.css\n# or\nyarn add instantsearch.css\n```\n\n## Available themes\n\nInstantSearch.css exposes two themes:\n\n- [Algolia](src/themes/algolia.scss)\n- [Satellite](src/themes/satellite.scss)\n\nIf you don't need a theme, you can still use the [reset styles](src/themes/reset.scss) to start from a clean slate.\n\n**The reset styles are included in all the provided themes.** You don't need to import them if you use a theme.\n\n## Browser support\n\nWe support the **last two versions of major browsers** (Chrome, Edge, Firefox, Safari).\n\n## Implemented versions\n\nInstantSearch.css is a living standard. This table tracks down the version implemented in each InstantSearch flavor.\n\n| Project             | Version |\n| ------------------- | ------- |\n| InstantSearch.js    | 7       |\n| React InstantSearch | 7       |\n| Vue InstantSearch   | 7       |\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nInstantSearch.css is [MIT licensed][license-url].\n\n<!-- Badges -->\n\n[version-svg]: https://img.shields.io/npm/v/instantsearch.css.svg?style=flat-square\n[package-url]: https://npmjs.org/package/instantsearch.css\n[ci-svg]: https://img.shields.io/circleci/project/github/algolia/instantsearch.svg?style=flat-square\n[ci-url]: https://circleci.com/gh/algolia/instantsearch\n[license-image]: http://img.shields.io/badge/license-MIT-green.svg?style=flat-square\n[website-svg]: https://img.shields.io/badge/website-instantsearchjs.netlify.app/specs-yellow?style=flat-square\n[website-url]: https://instantsearchjs.netlify.app/specs/\n[license-url]: LICENSE\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20InstantSearch.css\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20InstantSearch.css&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20InstantSearch.css\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20InstantSearch.css%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20InstantSearch.css%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20InstantSearch.css%22\n","packages/instantsearch.js/CLAUDE.md":"# instantsearch.js (core / vanilla JS)\n\nThe core package. **Every connector lives here and is consumed by all flavors** — React hooks and Vue components wrap these. A behavior change usually belongs in a connector here, not in a flavor package.\n\n## Layout\n\n- `src/connectors/<name>/connect<Pascal>.ts` — framework-agnostic logic. Signature: `connect<Widget>(renderFn, unmountFn)` returns a widget factory. Owns state, lifecycle, and search-param mutations. Exported from `src/connectors/index.ts`.\n- `src/widgets/<name>/<name>.tsx` — vanilla JS widget = connector + default template/rendering. Exported from `src/widgets/index.ts`.\n- `src/components/` — the **JS flavor's Preact components** (the widgets' markup). This is the _legacy_ home for widget layout; newer widgets pull their layout from the shared `instantsearch-ui-components` package instead, and some components here are now thin wrappers that import from it. Add **new shared** markup to `instantsearch-ui-components`, not here.\n- `src/lib/` — the InstantSearch runtime: lifecycle orchestration, routing, helper integration.\n- `src/lib/ai-lite/` — hand-written reimplementation of the Vercel AI SDK's UI layer (`UIMessage` parts, the SSE chunk protocol, the `Chat` state machine `connectChat` drives). It replaced `ai@^5.0.18` in #6880 and tracks nothing upstream: AI SDK fixes land on the `ai@6` line and never reach us. **Before changing its semantics, diff the matching file under [`vercel/ai/packages/ai/src/ui`](https://github.com/vercel/ai/tree/main/packages/ai/src/ui)** — the auto-continue predicate silently missed vercel/ai#9944 (don't resend provider-executed tool turns) for months. Deliberate gaps: no tool-approval flow, no metadata/data-part schema validation, no `finishReason`.\n- `src/types/` — public type definitions.\n\n## Working here\n\n```bash\nyarn workspace instantsearch.js build      # rollup → esm + cjs + umd + .d.ts\nyarn jest packages/instantsearch.js/src/connectors/<name>   # run one connector's tests (from repo root)\n```\n\n- Connectors return a typed render state — don't return untyped state (oxlint rule).\n- A new widget that only varies presentation should **reuse an existing connector** (e.g. `menuSelect` reuses `connectMenu`) and set a distinct `$$widgetType`; don't fork the connector.\n- Cross-flavor behavior is covered by `tests/common/connectors/<name>/`; flavor-agnostic logic tests go there, JS-specific ones stay in `__tests__/`.\n","packages/instantsearch.js/README.md":"<p align=\"center\">\n  <a href=\"https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/\">\n    <img alt=\"InstantSearch.js\" src=\"https://github.com/algolia/instantsearch/blob/master/.github/banner.png?raw=true\">\n  </a>\n\n  <p align=\"center\">\n    InstantSearch.js is a JavaScript library for building performant and instant search experiences with <a href=\"https://www.algolia.com/?utm_source=instantsearch.js&utm_campaign=repository\">Algolia</a>.\n  </p>\n</p>\n\n---\n\nInstantSearch.js is a vanilla JavaScript library that lets you create an instant-search result experience using [Algolia][algolia-website]’s search API. It is part of the InstantSearch family:\n\n**InstantSearch.js** | [React InstantSearch][instantsearch-github] | [Vue InstantSearch][instantsearch-github] | [React InstantSearch Native][instantsearch-github] | [InstantSearch Android][instantsearch-android-github] | [InstantSearch iOS][instantsearch-ios-github]\n\n<details>\n  <summary><strong>Table of contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Why](#why)\n- [Getting started](#getting-started)\n- [Installation](#installation)\n  - [TypeScript users](#typescript-users)\n- [Documentation](#documentation)\n- [Demos](#demos)\n- [Playground](#playground)\n- [Browser support](#browser-support)\n- [Troubleshooting](#troubleshooting)\n- [Contributing](#contributing)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## Why\n\nYou should be using InstantSearch if you want to:\n\n- Design search experiences with best practices\n- Customize your components at will\n- Remain independent from external frameworks\n\n## Getting started\n\nUsing InstantSearch.js is as simple as adding this JavaScript code to your page:\n\n```javascript\n// 1. Instantiate the search\nconst search = instantsearch({\n  indexName: 'instant_search',\n  searchClient: algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76'),\n});\n\nsearch.addWidgets([\n  // 2. Create an interactive search box\n  instantsearch.widgets.searchBox({\n    container: '#searchbox',\n    placeholder: 'Search for products',\n  }),\n\n  // 3. Plug the search results into the product container\n  instantsearch.widgets.hits({\n    container: '#products',\n    templates: {\n      item: (hit, { html, components }) => html`\n        <h3>${components.Highlight({ attribute: 'name', hit })}</h3>\n        <p>${components.Snippet({ attribute: 'description', hit })}</p>\n      `,\n    },\n  }),\n\n  // 4. Make the brands refinable\n  instantsearch.widgets.refinementList({\n    container: '#brand',\n    attribute: 'brand',\n  }),\n]);\n\n// 5. Start the search!\nsearch.start();\n```\n\n<p align=\"center\">\n  <a href=\"https://codesandbox.io/s/github/algolia/doc-code-samples/tree/master/InstantSearch.js/getting-started\" title=\"Edit on CodeSandbox\">\n    <img alt=\"Edit on CodeSandbox\" src=\"https://codesandbox.io/static/img/play-codesandbox.svg\">\n  </a>\n</p>\n\nTo learn more about the library, follow the [getting started](https://www.algolia.com/doc/guides/building-search-ui/getting-started/js/) guide or check how to [add it to your own project](https://www.algolia.com/doc/guides/building-search-ui/installation/js/).\n\n## Installation\n\n```sh\nnpm install instantsearch.js algoliasearch\n# or\nyarn add instantsearch.js algoliasearch\n```\n\n### TypeScript users\n\nTo use InstantSearch.js in a TypeScript environment, depending on your [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) version, you need to import different types.\n\n> You still need to import these types even if you don't use InstantSearch.js with [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript).\n\n#### `algoliasearch` v4.x\n\nThis version uses types provided by both `algoliasearch` and `@algolia/client-search`.\n\n```bash\nyarn add algoliasearch@4 @algolia/client-search\n```\n\n#### `algoliasearch` v3.x\n\n```bash\nyarn add @types/algoliasearch@3\n```\n\n> v3.x is deprecated and will soon no longer be supported.\n\n## Documentation\n\nThe documentation is available on the [Algolia website](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/).\n\n## Demos\n\n| E-commerce | Media | Travel |\n| --- | --- | --- |\n| <a href=\"https://instantsearchjs.netlify.app/examples/e-commerce/\"><img src=\"https://www.algolia.com/doc/assets/images/build-search-ui/demos/e-commerce-2c7ed6b6.png\" width=\"250\" alt=\"E-commerce demo preview\"></a> | <a href=\"https://instantsearchjs.netlify.app/examples/media/\"><img src=\"https://www.algolia.com/doc/assets/images/build-search-ui/demos/media-articles-abc153ab.png\" width=\"250\" alt=\"Media demo preview\"></a> | <a href=\"https://instantsearchjs.netlify.app/examples/tourism/\"><img src=\"https://instantsearchjs.netlify.app/examples/tourism/capture.png\" width=\"250\" alt=\"Tourism demo preview\"></a> |\n\nSee more demos [on the website](https://www.algolia.com/doc/guides/building-search-ui/resources/demos/js/).\n\n## Playground\n\nYou can get to know InstantSearch.js on [this playground](https://codesandbox.io/s/github/algolia/create-instantsearch-app/tree/templates/instantsearch.js).\n\nStart by [adding widgets](https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/js/) and tweaking the display. Once you feel familiar with the library, we recommend following the [getting started guide](https://www.algolia.com/doc/guides/building-search-ui/getting-started/js/).\n\n## Browser support\n\nWe support the **last two versions of major browsers** (Chrome, Edge, Firefox, Safari).\n\nPlease refer to the [browser support](https://www.algolia.com/doc/guides/building-search-ui/installation/js/#browser-support) section in the documentation to use InstantSearch.js on other browsers.\n\n## Troubleshooting\n\nEncountering an issue? Before reaching out to support, we recommend heading to our [FAQ](https://www.algolia.com/doc/guides/building-search-ui/troubleshooting/faq/js/) where you will find answers for the most common issues and gotchas with the library.\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nInstantSearch.js is [MIT licensed][license-url].\n\n<!-- Links -->\n\n[license-url]: LICENSE\n[algolia-website]: https://www.algolia.com/?utm_source=instantsearch.js&utm_campaign=repository\n[instantsearch-github]: https://github.com/algolia/instantsearch/\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20InstantSearch.js\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20InstantSearch.js&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20InstantSearch.js\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20InstantSearch.js%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20InstantSearch.js%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20InstantSearch.js%22\n","packages/instantsearch.js/scripts/transforms/README.md":"## Codemods\n\nThese codemods (code transformers) can be ran with [jscodeshift]((https://github.com/facebook/jscodeshift).\n\n### `addWidget-to-addWidgets`\n\nThis will replace calls of `addWidget(widget)` to `addWidgets([widget])`, as well as `removeWidget(widget) to `removeWidgets([widget])`.\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#addWidget-to-addWidgets' <path>\n```\n\n### Notes\n\nIf you are using a formatter or a linter, make sure to run its autofixing after this transformation, since code can be formatted differently after it has been transformed. In our repository, that would be:\n\n```\nyarn format\n```\n","packages/react-instantsearch-core/README.md":"<p align=\"center\">\n  <a href=\"https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/\">\n    <img alt=\"React InstantSearch\" src=\"https://github.com/algolia/instantsearch/blob/master/.github/react-instantsearch-banner.png?raw=true\">\n  </a>\n</p>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch-core](#react-instantsearch-core)\n  - [Installation](#installation)\n  - [Getting started](#getting-started)\n  - [API reference](#api-reference)\n  - [Documentation](#documentation)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch-core\n\nReact InstantSearch Core is an open-source UI library for React that lets you quickly build a search interface in your front-end application.\n\nInstantSearch’s goal is to help you implement awesome search experiences as smoothly as possible by providing a [complete search ecosystem](https://algolia.com/doc/guides/getting-started/how-algolia-works/#the-full-ecosystem). InstantSearch tackles an important part of this vast goal by providing front-end primitives that you can assemble into unique search interfaces.\n\n<p align=\"center\">\n  <a href=\"https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/default-themes\" title=\"Edit on CodeSandbox\">\n    <img alt=\"Edit on CodeSandbox\" src=\"https://codesandbox.io/static/img/play-codesandbox.svg\">\n  </a>\n</p>\n\n> Note: `react-instantsearch-core` exports renderless components and hooks which can be used for both web and React Native. If you are using React in a web project, we recommend using the package `react-instantsearch` instead, as it includes complete components that render to the DOM.\n\n## Installation\n\nReact InstantSearch Core is available on the npm registry. It relies on [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) to communicate with Algolia APIs.\n\n```sh\nyarn add algoliasearch react-instantsearch-core\n# or\nnpm install algoliasearch react-instantsearch-core\n```\n\n## Getting started\n\nReact InstantSearch Core is a headless React library that lets you create an instant search results experience using Algolia’s search API.\n\nCheck out our [**Getting Started guide**](https://algolia.com/doc/guides/building-search-ui/getting-started/react/) to start implementing a full-featured search experience with React InstantSearch Core.\n\n## API reference\n\nCheck out the [**API reference**](https://www.algolia.com/doc/api-reference/widgets/react/).\n\n## Documentation\n\nThe documentation is available on [algolia.com/doc](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/).\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","packages/react-instantsearch-nextjs/CLAUDE.md":"# react-instantsearch-nextjs (Next.js App Router)\n\nSSR integration for the **App Router** (RSC + streaming). Peer-deps `next` + `react-instantsearch` — it **composes** react-instantsearch, never reimplements search. This is the non-trivial Next.js package; the subtle part is server-render + hydration.\n\n## Public surface\n\n- `InstantSearchNext` (`src/InstantSearchNext.tsx`) — the entry component, a drop-in for `<InstantSearch>` inside the App Router. It does SSR **for you**, so you don't hand-wire the plain-react-instantsearch `getServerState` + `InstantSearchSSRProvider` dance. Props: `routing` (boolean | object) and an optional `instance`.\n\n## How the SSR machinery fits together\n\n- `InitializePromise.ts` + `TriggerSearch.ts` — run the search **during the server (RSC) render** so results are available before HTML is sent.\n- `createInsertHTML.tsx` + `htmlEscape.ts` — serialize that initial server state and **inject it into the streamed HTML** for hydration. `htmlEscape` is a **safety boundary** (escapes the injected payload so it can't break out of the script/HTML context) — don't bypass it when touching the insert path.\n- `useInstantSearchRouting.ts`, `useNextHeaders.tsx`, `useDynamicRouteWarning.ts` — routing via `next/navigation` (not `next/router`). Dynamic routes are warned about (`useDynamicRouteWarning`) because they interact poorly with InstantSearch URL sync.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch-nextjs build\nyarn jest packages/react-instantsearch-nextjs/src/__tests__/<name>   # unit tests, from repo root\nyarn workspace react-instantsearch-nextjs test:e2e                   # Playwright (App Router)\n```\n\n- **Hydration is the failure mode.** A change to the initialize/trigger/insert path can produce server/client markup mismatch — verify there's no hydration warning and that the first paint already reflects server results.\n- e2e lives in `__tests__/e2e/` (Playwright). Browser back/forward on dynamic App Router routes needs file-level `slowMo` in headless mode — see `.claude/rules/e2e.md`.\n- Treat SSR/RSC/hydration work here as **reasoning-heavy** — it's the one corner of the React surface that isn't bounded widget wiring.\n","packages/react-instantsearch-nextjs/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch-nextjs](#react-instantsearch-nextjs)\n  - [Installation](#installation)\n  - [Usage](#usage)\n  - [API](#api)\n    - [`<InstantSearchNext>`](#instantsearchnext)\n    - [`routing` prop](#routing-prop)\n  - [Troubleshooting](#troubleshooting)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch-nextjs\n\nThis package provides server-side rendering for [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) that is compatible with [Next.js 13 App Router](https://nextjs.org/docs/app).\n\n## Installation\n\n```sh\nyarn add react-instantsearch-nextjs\n# or with npm\nnpm install react-instantsearch-nextjs\n```\n\n## Usage\n\n> [!NOTE] You can check this documentation on [Algolia's Documentation website](https://www.algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react/#app-router-experimental).\n\nYour search component must be in its own file, and it shouldn't be named `page.js` or `page.tsx`.\n\nTo render the component in the browser and allow users to interact with it, include the \"use client\" directive at the top of your code.\n\n```diff\n+'use client';\nimport { liteClient as algoliasearch } from 'algoliasearch/lite';\nimport {\n  InstantSearch,\n  SearchBox,\n} from 'react-instantsearch';\n\nconst searchClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');\n\nexport function Search() {\n  return (\n    <InstantSearch indexName=\"YourIndexName\" searchClient={searchClient}>\n      <SearchBox />\n      {/* other widgets */}\n    </InstantSearch>\n  );\n}\n```\n\nImport the `<InstantSearchNext>` component from the `react-instantsearch-nextjs` package, and replace the [`<InstantSearch>`](https://www.algolia.com/doc/api-reference/widgets/instantsearch/react/) component with it, without changing the props.\n\n```diff\n'use client';\nimport { liteClient as algoliasearch } from 'algoliasearch/lite';\nimport {\n- InstantSearch,\n  SearchBox,\n} from 'react-instantsearch';\n+import { InstantSearchNext } from 'react-instantsearch-nextjs';\n\nconst searchClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');\n\nexport function Search() {\n  return (\n-   <InstantSearch indexName=\"YourIndexName\" searchClient={searchClient}>\n+   <InstantSearchNext indexName=\"YourIndexName\" searchClient={searchClient}>\n      <SearchBox />\n      {/* other widgets */}\n\n-   </InstantSearch>\n+   </InstantSearchNext>\n  );\n}\n```\n\nTo serve your search page at `/search`, create an `app/search` directory. Inside it, create a `page.js` file (or `page.tsx` if you're using TypeScript).\n\nMake sure to [configure your route segment to be dynamic](https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic) so that Next.js generates a new page for each request.\n\n```jsx\n// app/search/page.js or app/search/page.tsx\nimport { Search } from './Search'; // change this with the path to your <Search> component\n\nexport const dynamic = 'force-dynamic';\n\nexport default function Page() {\n  return <Search />;\n}\n```\n\nYou can now visit `/search` to see your server-side rendered search page.\n\n## API\n\n### `<InstantSearchNext>`\n\nThe `<InstantSearchNext>` component is a drop-in replacement for the `<InstantSearch>` component. It accepts the same props, and it renders the same UI.\n\nYou can check the [InstantSearch API reference](https://www.algolia.com/doc/api-reference/widgets/instantsearch/react/) for more information.\n\n### `routing` prop\n\nAs with the `<InstantSearch>` component, you can pass a `routing` prop to the `<InstantSearchNext>` component to customize the routing behavior. The difference here is that `routing.router` takes [the same options as the `historyRouter`](https://www.algolia.com/doc/api-reference/widgets/history-router/react/).\n\n## Troubleshooting\n\nIf you're experiencing issues, please refer to the [**Need help?**](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/#need-help) section of the docs, or [open a new issue](https://github.com/algolia/instantsearch.js/issues/new?assignees=&labels=triage&template=BUG_REPORT.yml).\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","packages/react-instantsearch-router-nextjs/CLAUDE.md":"# react-instantsearch-router-nextjs (Next.js Pages Router)\n\nA **thin routing adapter** for the **Pages Router** — not a full SSR layer. It wraps InstantSearch's history router so URL sync plays well with `next/router`. The actual SSR is the standard react-instantsearch `getServerState` + `InstantSearchSSRProvider` pattern in your app, **not** here.\n\n## Public surface\n\n- `createInstantSearchRouterNext({ singletonRouter, serverUrl?, ... })` (`src/index.ts`) — returns a `Router` you pass to `<InstantSearch routing={{ router }}>`. Tagged `$$type: 'ais.nextjs'`.\n\n## What it actually does (and why)\n\n- Builds on `instantsearch.js/es/lib/routers/history`; on the server (`typeof window === 'undefined'`) it returns a simpler router reading from `serverUrl`.\n- `push` uses **`shallow: true`** so refinements don't re-run `getServerSideProps`, and strips the locale (`stripLocaleFromUrl`) to avoid Next i18n path errors.\n- Hooks `routeChangeComplete` + `beforePopState` to trigger SSR **only** on real page changes (back/forward to a different page), not on every refinement. `beforePopState`/`beforeStart`/`beforeDispose`/`routerOptions` let consumers compose their own logic.\n- `stripLocaleFromUrl` (`src/utils/`) handles i18n locale prefixes in the URL.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch-router-nextjs build\nyarn workspace react-instantsearch-router-nextjs test:e2e   # Playwright (Pages Router)\n```\n\n- Surface is small — most behavior lives in the shared history router (`instantsearch.js`) and react-instantsearch SSR core. Keep this package to the **Next-specific routing glue**.\n- The tricky cases are i18n/locale paths and back/forward navigation deciding whether to re-SSR. e2e in `__tests__/e2e/` (Playwright); see `.claude/rules/e2e.md`.\n","packages/react-instantsearch-router-nextjs/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch-router-nextjs](#react-instantsearch-router-nextjs)\n  - [Installation](#installation)\n  - [Usage](#usage)\n  - [API](#api)\n  - [Troubleshooting](#troubleshooting)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch-router-nextjs\n\nThis package is a router for [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) that is compatible with [Next.js](https://nextjs.org/) routing.\n\n> :warning: **This function cannot be used in conjunction with [`getStaticProps()`](https://nextjs.org/docs/api-reference/data-fetching/get-static-props). Use `getServerSideProps()` or client-side rendering instead.**\n\n## Installation\n\n```sh\nyarn add react-instantsearch-router-nextjs\n# or with npm\nnpm install react-instantsearch-router-nextjs\n```\n\n## Usage\n\nYou need to pass the Next.js router singleton that you can import from `next/router`.\n\nIf you are doing SSR with the `getServerState` and `InstantSearchSSRProvider` from `react-instantsearch/server`, you need to pass the `url` prop to `createInstantSearchRouterNext`'s `serverUrl` option :\n\n```js\nimport singletonRouter from 'next/router';\nimport { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';\n\nexport default function Page({ serverState, url }) {\n  return (\n    <InstantSearchSSRProvider {...serverState}>\n      <InstantSearch\n        searchClient={searchClient}\n        indexName=\"instant_search\"\n        routing={{\n          router: createInstantSearchRouterNext({\n            singletonRouter,\n            serverUrl: url,\n          }),\n        }}\n      >\n        {/* ... */}\n      </InstantSearch>\n    </InstantSearchSSRProvider>\n  );\n}\n```\n\nIf you are not doing SSR but only CSR, you can omit the `serverUrl` option:\n\n```js\nimport singletonRouter from 'next/router';\nimport { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';\n\nexport default function Page() {\n  return (\n    <InstantSearch\n      searchClient={searchClient}\n      indexName=\"instant_search\"\n      routing={{ router: createInstantSearchRouterNext({ singletonRouter }) }}\n    >\n      {/* ... */}\n    </InstantSearch>\n  );\n}\n```\n\nLastly, if you had custom routing logic in your app, you can pass it to the `createInstantSearchRouterNext`'s `routerOptions` option:\n\n```js\nimport singletonRouter from 'next/router';\nimport { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';\n\nexport default function Page({ serverState, url }) {\n  return (\n    {/* ... */}\n      <InstantSearch\n        searchClient={searchClient}\n        indexName=\"instant_search\"\n        routing={{\n          router: createInstantSearchRouterNext({\n            singletonRouter,\n            serverUrl: url,\n            routerOptions: {\n              createURL: /* ... */,\n              parseURL: /* ... */,\n            },\n          }),\n           // if you are using a custom `stateMapping` you can still pass it :\n          stateMapping: /* ... */,\n        }}\n      >\n        {/* ... */}\n      </InstantSearch>\n    {/* ... */}\n  );\n}\n```\n\n## API\n\nThe options are :\n\n- `singletonRouter: SingletonRouter`: the required Next.js router singleton.\n- `serverUrl?: string`: the URL of the page on the server. Required if you are doing SSR with `getServerState` and `InstantSearchSSRProvider`.\n- `routerOptions?: RouterOptions`: the options passed to the `history` router. See [the documentation](https://www.algolia.com/doc/api-reference/widgets/history-router/js/) for more details.\n\nFor troubleshooting purposes, some other options are available :\n\n- `beforeStart?: (onUpdate: () => void) => void`: a function called before the router starts. You can use it to inform InstantSearch to update on router events by calling `onUpdate`.\n- `beforeDispose?: () => void`: a function called before the router disposes. You can use it to clean up handlers you added in `beforeStart`.\n- `beforePopState?: (options: { state: NextHistoryState, ownBeforePopState: BeforePopStateCallback, libraryBeforePopState: BeforePopStateCallback }) => boolean`: a function used by the Next.js router to know whether it should trigger SSR when using back/forward buttons. You can use it to override the default one by writing your own logic. The `ownBeforePopState` is the pre-existing handler that you may have set yourself, and the `libraryBeforePopState` is the default one from the library.\n\n## Troubleshooting\n\nIf you're experiencing issues, please refer to the [**Need help?**](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/#need-help) section of the docs, or [open a new issue](https://github.com/algolia/instantsearch/issues/new?assignees=&labels=triage&template=BUG_REPORT.yml).\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","packages/react-instantsearch/CLAUDE.md":"# react-instantsearch\n\nReact DOM bindings. This package = **hooks (from `react-instantsearch-core`) + UI markup (from `instantsearch-ui-components`)**. The search logic itself is the connector in `instantsearch.js`; don't reimplement it here.\n\n## Layer split (where each thing goes)\n\n- `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts` — the **hook** that wraps the `instantsearch.js` connector. Headless, no DOM. Exported from that package's `src/index.ts`.\n- `packages/react-instantsearch/src/widgets/<Pascal>.tsx` — the **component**: calls the hook, renders UI. Exported from `src/widgets/index.ts`.\n- `packages/react-instantsearch/src/ui/<Pascal>.tsx` — local presentational component, only when the markup isn't already in the shared `instantsearch-ui-components` package.\n\nSo adding/changing a React widget often touches `react-instantsearch-core` (hook) **and** `react-instantsearch` (component) — keep them in sync.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch build\nyarn workspace react-instantsearch-core build\nyarn jest packages/react-instantsearch/src/widgets/__tests__/<Pascal>   # from repo root\n```\n\n- Uses React 19 / `@testing-library/react` for tests, co-located in `__tests__/`.\n- Cross-flavor behavior lives in `tests/common/widgets/<name>/`; register it in this package's `common-widgets.test.*`.\n- Prefer shared UI from `instantsearch-ui-components` (consume `create<Name>Component` with React's `createElement`) over new local `ui/` components. **Creating or changing a shared component is the `instantsearch-ui-components` package's job** — do that there, then consume it here. Add a local `src/ui/` component only when the markup truly isn't shareable.\n","packages/react-instantsearch/README.md":"<p align=\"center\">\n  <a href=\"https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/\">\n    <img alt=\"React InstantSearch\" src=\"https://github.com/algolia/instantsearch/blob/master/.github/react-instantsearch-banner.png?raw=true\">\n  </a>\n</p>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch](#react-instantsearch)\n  - [Why](#why)\n  - [Installation](#installation)\n  - [Getting started](#getting-started)\n  - [Documentation](#documentation)\n  - [Playground](#playground)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch\n\nReact InstantSearch is an open-source React library that lets you create an instant search result experience using [Algolia][algolia-website]’s search API. It is part of the InstantSearch family:\n\n**React InstantSearch** | [InstantSearch.js][instantsearch-github] | [Vue InstantSearch][instantsearch-github] | [InstantSearch Android][instantsearch-android-github] | [InstantSearch iOS][instantsearch-ios-github]\n\n## Why\n\nYou should be using React InstantSearch if you want to:\n\n- Design search experiences with best practices\n- Customize your components at will\n- Follow React principles\n\nNote: If you are working with React Native, or otherwise do not use the DOM, check out `react-instantsearch-core` instead.\n\n## Installation\n\nReact InstantSearch is available on the npm registry. It relies on [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) to communicate with Algolia APIs.\n\n```sh\nyarn add algoliasearch react-instantsearch\n# or\nnpm install algoliasearch react-instantsearch\n```\n\n## Getting started\n\nUsing React InstantSearch is as simple as adding these components to your app:\n\n```javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { liteClient as algoliasearch } from 'algoliasearch/lite';\nimport { InstantSearch, SearchBox, Hits } from 'react-instantsearch';\n\nconst searchClient = algoliasearch(\n  'latency',\n  '6be0576ff61c053d5f9a3225e2a90f76'\n);\n\nconst App = () => (\n  <InstantSearch indexName=\"bestbuy\" searchClient={searchClient}>\n    <SearchBox />\n    <Hits />\n  </InstantSearch>\n);\n```\n\n<p align=\"center\">\n  <a href=\"https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/default-theme\" title=\"Edit on CodeSandbox\">\n    <img alt=\"Edit on CodeSandbox\" src=\"https://codesandbox.io/static/img/play-codesandbox.svg\">\n  </a>\n</p>\n\nTo learn more about the library, follow the [getting started guide][doc-getting-started].\n\n## Documentation\n\nThe documentation is available on [algolia.com/doc][doc].\n\n## Playground\n\nYou can get to know React InstantSearch on [this playground][doc-playground].\n\nStart by [adding components][doc-getting-started] and tweaking the display. Once you get more familiar with the library, you can learn more advanced concepts in [our guides][doc-guides].\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[doc]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/\n[doc-getting-started]: https://www.algolia.com/doc/guides/building-search-ui/getting-started/react/\n[doc-guides]: https://www.algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react/\n[doc-playground]: https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/default-theme\n[algolia-website]: https://www.algolia.com/\n[instantsearch-github]: https://github.com/algolia/instantsearch\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","packages/vue-instantsearch/CLAUDE.md":"# vue-instantsearch\n\nVue 2 **and** Vue 3 bindings (single source, built for both). Components wrap the `instantsearch.js` connectors — the logic isn't reimplemented here.\n\n## Layout\n\n- `src/components/<Pascal>.vue` (SFC) or `.js` (render function) — the component wrapping a connector. Exported from `src/widgets.js`.\n- `src/mixins/` — shared connector-wiring logic reused across components.\n- To reuse shared markup, import the `create<Name>Component` factory from `instantsearch-ui-components` and render it inside a **`renderCompat`** render function, passing Vue's `h` as `createElement` — e.g. `render: renderCompat((h) => h(createHitsComponent({ createElement: h }), { … }))` (see `src/components/Hits.js`). `renderCompat` (`src/util/vue-compat/`) is the Vue 2/3 render-fn shim. **Creating or changing the shared component itself is the `instantsearch-ui-components` package's job** — do that there, then consume the factory here.\n\n## Working here\n\n```bash\nyarn workspace vue-instantsearch build\nyarn jest packages/vue-instantsearch/src/components/__tests__/<Pascal>   # from repo root\n```\n\n- Vue 2/3 dual support: run `./scripts/prepare-vue3.js` (root `yarn prepare-vue3`) when switching the working tree to Vue 3. Don't assume one Vue version — changes must work for both.\n- Cross-flavor behavior lives in `tests/common/widgets/<name>/`; register it in this package's `common-widgets.test.*`.\n","packages/vue-instantsearch/README.md":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Vue InstantSearch](#vue-instantsearch)\n- [Documentation](#documentation)\n- [Installation](#installation)\n- [Troubleshooting](#troubleshooting)\n- [Contributing, dev, release](#contributing-dev-release)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n[![Vue InstantSearch logo][logo]][website]\n\nInstantSearch projects: **Vue InstantSearch** | [InstantSearch.js][instantsearch-github] | [React InstantSearch][instantsearch-github] | [InstantSearch Android][instantsearch-android-github] | [InstantSearch iOS][instantsearch-ios-github].\n\n## Vue InstantSearch\n\n> ⚡ Lightning-fast search for Vue.js apps\n\nBuilt by [Algolia][algolia-website].\n\nThis repository holds the code for the Vue InstantSearch project.\n\n## Documentation\n\nThere's a dedicated documentation available at [www.algolia.com/doc/guides/building-search-ui/getting-started/vue][website].\n\n## Installation\n\nVue InstantSearch is available in the npm registry. Install it:\n\n```sh\n# with npm\nnpm install --save vue-instantsearch\n\n# with yarn\nyarn add vue-instantsearch\n```\n\nTo learn more about the usage, follow our [getting started guide][getting-started-guide].\n\n## Troubleshooting\n\nEncountering an issue? Before reaching out to support, we recommend heading to our [FAQ](https://www.algolia.com/doc/guides/building-search-ui/troubleshooting/faq/vue/) where you will find answers for the most common issues and gotchas with the library.\n\n## Contributing, dev, release\n\nWe welcome all contributors, from casual to regular. You are only one command away to start the developer environment, [read our CONTRIBUTING guide](CONTRIBUTING.md).\n\n[logo]: vue-instantsearch-readme.png\n[website]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/vue/\n[getting-started-guide]: https://www.algolia.com/doc/guides/building-search-ui/getting-started/vue/\n[algolia-website]: https://www.algolia.com/\n[instantsearch-github]: https://github.com/algolia/instantsearch\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"CLAUDE.md","isInternal":false,"tokens":4,"sizeBytes":9},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# InstantSearch monorepo\n\nSearch UI libraries for Algolia across three flavors (vanilla JS, React, Vue), sharing one connector + UI-component core. Yarn 4 workspaces + Lerna (independent versioning).\n\n## Repo map\n\n| Package | What it is |\n| --- | --- |\n| `packages/instantsearch.js` | **Core.** Vanilla JS library. **All connectors live here** (`src/connectors/`) and are shared by every flavor. |\n| `packages/react-instantsearch-core` | Headless React: hooks (`useSearchBox`, …) that wrap connectors. Framework-agnostic (web/RN). |\n| `packages/react-instantsearch` | React DOM widgets = hooks + UI from `instantsearch-ui-components`. |\n| `packages/vue-instantsearch` | Vue 2/3 components wrapping connectors. |\n| `packages/instantsearch-ui-components` | **Shared UI layer.** Framework-agnostic widget markup + `ais-*` classes via factory components (`create<Name>Component({ createElement, Fragment })`); reused by React directly, Vue via `renderCompat`, JS via the Preact renderer. Newer widgets get their layout here; older ones still define it in `instantsearch.js/src/components/`. |\n| `packages/algoliasearch-helper` | Low-level search-parameter/state manager underneath connectors. Mature, separately-versioned; **rarely the place for a change** — fix the connector instead. See its `CLAUDE.md`. |\n| `packages/instantsearch.css` | Default themes. |\n| `packages/react-instantsearch-nextjs` / `-router-nextjs` | Next.js App Router / Pages Router integrations. |\n| `packages/create-instantsearch-app`, `instantsearch-cli`, `instantsearch-codemods` | Scaffolding / CLI / migration tooling. |\n\nArchitecture in one line: **connector (logic, in instantsearch.js) → flavor wrapper (JS widget / React hook+component / Vue component) → shared UI components**. Change behavior in the connector; change markup in the UI layer.\n\n## Where work goes (per layer)\n\nBecause behavior lives in the connector and the flavors only wrap it, most changes have a natural home — and the per-package `CLAUDE.md` files carry each layer's conventions:\n\n| Work in… | Package |\n| --- | --- |\n| Connectors, JS widgets, runtime (`src/lib`), legacy Preact components (`src/components`), types | `packages/instantsearch.js` |\n| Shared framework-agnostic markup / `ais-*` classes | `packages/instantsearch-ui-components` |\n| React widgets/hooks + Next.js integrations | `react-instantsearch` / `react-instantsearch-core` / `*-nextjs` |\n| Vue components (Vue 2 **and** 3) | `vue-instantsearch` |\n\nFor a **cross-flavor change**, settle the contract in the connector first, then update the React and Vue wrappers, then the common tests. If the change is to **shared markup/layout** (not behavior), it lives in `instantsearch-ui-components` and the flavors consume the result — a class/structure change there ripples to all flavors and `instantsearch.css` at once.\n\n**Adding or surfacing a connector option is always cross-flavor work** — settle the contract in the connector first, then thread it through React **and** Vue, then add/extend common tests. Don't wire one flavor and stop. The **`/expose-option <connector> <option>`** command walks this recipe; **`/preflight`** runs the pre-push lint/types/tests/commit check; the `/port-widget` skill adds a brand-new widget across flavors.\n\n## Commands\n\nNode `20.19.0` (`.nvmrc`). Use Yarn (v4, pinned by `packageManager`).\n\n```bash\n# Build\nyarn build                      # all packages (lerna)\nyarn workspace <pkg> build      # single package, e.g. yarn workspace instantsearch.js build\n\n# Unit tests (Jest, jsdom)\nyarn jest <path-or-pattern>     # fastest loop: run one file/pattern at root\nyarn jest packages/<pkg>        # one package's suite (flavor pkgs have no `test` script — scope jest by path)\nyarn jest common-widgets        # shared cross-flavor widget suites (JS+React+Vue); -connectors for connectors\nyarn jest common-widgets -t \"Chat widget common tests\"   # scope to one widget (label = \"<Widget> widget common tests\")\nyarn test                       # everything (slow)\n\n# Lint (oxlint) + format (oxfmt) + types\nyarn lint:changed               # lint only changed files — use this while iterating\nyarn lint:fix                   # auto-fix\nyarn format                     # format the repo; yarn format:check only reports\nyarn type-check                 # tsc + per-package (also :v3 / :v4 for legacy algoliasearch)\n\n# E2E — see .claude/rules/e2e.md (Playwright). Examples must be built first:\nyarn website:examples && E2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n```\n\n## Conventions\n\n- **TypeScript strict.** No implicit any; unused vars/params error. Avoid `for-in`/`for-of` and `async` in library code (oxlint enforces).\n- **Tests** co-locate in `__tests__/` next to source — Jest picks up _any_ file under `__tests__/` (the default `testMatch`), so some legacy tests are plain `*.js`; name new ones `*.test.ts(x)`. Prefer focused assertions; use inline snapshots sparingly (initial render only). Mocks/helpers come from `@instantsearch/mocks` and `@instantsearch/testutils`.\n- **Cross-flavor tests** live in `tests/common/{widgets,connectors}/<name>/` and each flavor registers them in its `common-widgets.test.*` / `common-connectors.test.*`. Run with `yarn jest common-widgets` (all three flavors) and scope to one widget via `-t \"<Widget> widget common tests\"`. This is the primary test surface for newer widgets like Autocomplete and Chat. See `tests/common/README.md`.\n- **Commits: Conventional Commits** — `type(scope): description`, scope = widget/connector or topic (`deps`, `ci`). e.g. `fix(searchbox): increase magnifying glass size`, `feat(hits): add custom rendering`. Reference issues with `fix #1234` in the body.\n- **Branches:** target `master`; `vX` branches are critical-fix-only. Branch names: `fix/<issue>`, `feat/<name>`.\n- **Releases** are automated via Ship.js (`yarn release`); changelogs are generated from commits — don't hand-edit them.\n- **Agent workflows that write to another repo** (`.github/workflows/docs-*.yml`) must run with the working directory _inside_ that repo. A `CLAUDE.md`/`AGENTS.md` in a workspace subdirectory is never loaded, so the target repo's conventions get silently ignored — reach the source of this repo via `--add-dir` instead. Same for `--allowedTools`: `Bash(git diff:*)` matches, `Bash(git diff *)` matches nothing, and every denied call burns a turn.\n\n## Keep these docs alive\n\nIf you discover something durable and non-obvious while working — a gotcha that cost you a debugging detour, a convention, a non-obvious file location, a cross-flavor constraint — **propose** adding it to the right doc (package `CLAUDE.md` if package-specific, this file if repo-wide, `.claude/rules/e2e.md` for e2e). Surface the proposed edit for the user to approve rather than editing silently. Bar: it must generalize beyond the current task (same bar as a good code comment) and not already be documented.\n\n## Reference docs (read before relevant work — don't duplicate here)\n\n- `CONTRIBUTING.md` — full process, commit/branch rules, package-import flow.\n- `.claude/rules/e2e.md` — E2E testing (Playwright helpers, flavors, debugging).\n- `.claude/skills/port-widget/` — adding a widget across all three flavors (the `/port-widget` skill).\n- `tests/common/README.md` — shared cross-flavor test architecture.\n- Per-package `CLAUDE.md` in `instantsearch.js`, `instantsearch-ui-components`, `react-instantsearch`, `vue-instantsearch` for package-specific layout.\n","isInternal":false,"tokens":1854,"sizeBytes":7492},{"name":"e2e.md","path":".claude/rules/e2e.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/.claude/rules/e2e.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\npaths:\n  - 'tests/e2e/**'\n  - '.github/workflows/e2e.yml'\n  - '.github/workflows/ci.yml'\n  - 'website/serve.json'\n  - 'examples/*/e-commerce*/**'\n  - 'packages/react-instantsearch-nextjs/playwright.config.ts'\n  - 'packages/react-instantsearch-nextjs/__tests__/e2e/**'\n  - 'packages/react-instantsearch-router-nextjs/playwright.config.ts'\n  - 'packages/react-instantsearch-router-nextjs/__tests__/e2e/**'\n---\n\n# E2E Testing Guide\n\n## Test Locations and Frameworks\n\n| Location | Framework | Purpose |\n| --- | --- | --- |\n| `tests/e2e/playwright/` | Playwright | Main e2e suite for all InstantSearch flavors |\n| `tests/e2e/` (wdio files) | WebdriverIO | IE11 tests only (via Sauce Labs) |\n| `packages/react-instantsearch-nextjs/__tests__/e2e/` | Playwright | App Router Next.js e2e tests |\n| `packages/react-instantsearch-router-nextjs/__tests__/e2e/` | Playwright | Pages Router Next.js e2e tests |\n\n`.github/workflows/e2e.yml` runs on a weekday schedule and is called from `ci.yml` on pushes to master, never on pull requests. Run the suites locally before you push.\n\n## Running Tests Locally\n\n### Main E2E Suite (Playwright)\n\n**Prerequisites:** Examples must be built first:\n\n```bash\nyarn website:examples\n```\n\n```bash\n# Run all Playwright tests (all flavors, chromium + firefox)\nyarn test:e2e\n\n# Run tests for a specific flavor\nE2E_FLAVOR=react yarn test:e2e\n\n# Run tests for a specific browser (faster local dev)\nE2E_BROWSER=chromium yarn test:e2e\n\n# Combine both for fastest local iteration\nE2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n\n# Run tests with UI mode for debugging\nyarn workspace @instantsearch/e2e-tests test:playwright --ui\n```\n\n### Next.js Package Tests\n\n```bash\n# App Router (react-instantsearch-nextjs)\nyarn workspace react-instantsearch-nextjs test:e2e\n\n# Pages Router (react-instantsearch-router-nextjs)\nyarn workspace react-instantsearch-router-nextjs test:e2e\n\n# Run with list reporter (outputs to terminal instead of opening browser)\nyarn workspace react-instantsearch-nextjs test:e2e --reporter=list\nyarn workspace react-instantsearch-router-nextjs test:e2e --reporter=list\n```\n\n### IE11 Tests (Requires Sauce Labs credentials)\n\n```bash\n# Set environment variables first\nexport SAUCE_USERNAME=your_username\nexport SAUCE_ACCESS_KEY=your_access_key\n\nyarn test:e2e:ie11\n```\n\n## Writing Playwright Tests\n\n### Test Structure\n\n```typescript\nimport { test, expect } from '../fixtures';\n\ntest.describe('feature name', () => {\n  test('does something specific', async ({ page, helpers }) => {\n    // Use '.' to navigate to baseURL, NOT '/' (which goes to root)\n    await page.goto('.');\n\n    // Use helpers for common InstantSearch interactions\n    await helpers.clickRefinementListItem('Apple');\n    await helpers.setSearchBoxValue('macbook');\n\n    // Use Playwright's built-in assertions\n    await expect(page.locator('.ais-Hits-item')).toHaveCount(16);\n  });\n});\n```\n\n### Available Helper Methods\n\nThe `helpers` fixture provides these methods. Full signatures in `tests/e2e/playwright/fixtures.ts`.\n\n- **RefinementList:** `clickRefinementListItem(label)`, `getSelectedRefinementListItem()`\n- **SearchBox:** `setSearchBoxValue(value)`, `getSearchBoxValue()`\n- **Hits:** `getHitsTitles()`\n- **HierarchicalMenu:** `clickHierarchicalMenuItem(label)`, `getSelectedHierarchicalMenuItems()`\n- **RangeSlider:** `dragRangeSliderLowerBoundTo(value)`, `dragRangeSliderUpperBoundTo(value)`, `getRangeSliderLowerBoundValue()`, `getRangeSliderUpperBoundValue()`\n- **Pagination:** `clickPage(number)`, `clickNextPage()`, `clickPreviousPage()`, `getCurrentPage()`\n- **ToggleRefinement:** `clickToggleRefinement()`, `getToggleRefinementStatus()`\n- **RatingMenu:** `clickRatingMenuItem(label)`, `getSelectedRatingMenuItem()`. `label` is the link's `aria-label`, such as `4 & up`.\n- **SortBy:** `setSortByValue(label)`, `getSortByValue()`\n- **HitsPerPage:** `setHitsPerPage(label)`, `getHitsPerPage()`\n- **ClearRefinements:** `clickClearRefinements()`\n- **Any selector:** `waitForElement(selector)`, `getTextFromSelector(selector)`\n\n### Best Practices\n\n1. **Use helpers for InstantSearch interactions** - They handle waiting for URL changes automatically\n2. **Prefer Playwright's auto-waiting assertions** - Use `await expect(locator).toHaveText('...')` instead of manual waits\n3. **Avoid `page.waitForTimeout()`** - Use proper waits like `waitForURL()` or `expect.poll()`\n4. **Use locator chaining** - `page.locator('.parent').locator('.child')`\n5. **Use `'.'` for baseURL navigation** - `page.goto('.')` navigates to the baseURL path; `page.goto('/')` goes to the root domain (common mistake)\n6. **Prefer relative paths** - For routes like `search/category/?query=test`, use paths without leading `/`\n7. **Avoid hardcoded test data** - The Algolia index data can change. Instead of checking exact product names, verify behavior (e.g., results changed, count > 0, URL updated)\n8. **Wait for results after navigation** - After pagination or filter changes, wait for new results to load before asserting\n9. **Don't use `scrollIntoViewIfNeeded()`** - Playwright auto-scrolls elements into view before clicking\n10. **Don't add redundant waits after URL waits** - If you wait for URL change, the UI has already updated; no need to also wait for element visibility\n\n## Flavor System\n\nThe main e2e suite tests multiple InstantSearch flavors:\n\n- `js` - instantsearch.js with vanilla JS\n- `js-umd` - instantsearch.js UMD build\n- `react` - React InstantSearch\n- `vue` - Vue InstantSearch\n\nTests run against example apps in `website/examples/{flavor}/e-commerce/`.\n\nTo run a single flavor:\n\n```bash\nE2E_FLAVOR=react yarn test:e2e\n```\n\n## IE11 Considerations\n\nIE11 tests are kept in WebdriverIO because Playwright doesn't support IE11. These tests:\n\n- Only run `js` and `js-umd` flavors\n- Require Sauce Labs for remote IE11 browser\n- Use the same test specs in `tests/e2e/specs/` and helpers in `tests/e2e/helpers/`\n\n## Debugging\n\n### Playwright\n\n```bash\n# Run with headed browser\nyarn workspace @instantsearch/e2e-tests test:playwright --headed\n\n# Run with UI mode (interactive debugging)\nyarn workspace @instantsearch/e2e-tests test:playwright --ui\n\n# Run specific test file\nyarn workspace @instantsearch/e2e-tests test:playwright specs/brand-and-query.spec.ts\n\n# Debug mode with inspector\nPWDEBUG=1 yarn workspace @instantsearch/e2e-tests test:playwright\n```\n\n### View Test Reports\n\nAfter running tests, open the HTML report:\n\n```bash\nnpx playwright show-report tests/e2e/playwright/playwright-report\n```\n\n## Troubleshooting\n\n### Tests timeout waiting for elements\n\n1. **Check if examples are built**: Run `yarn website:examples` before running tests\n2. **Check if the server starts**: The webServer uses port 3456 (not 5000, which conflicts with macOS AirPlay)\n3. **Check navigation**: Use `page.goto('.')` not `page.goto('/')` to navigate to baseURL\n\n### Port conflicts on macOS\n\nOn macOS, port 5000 is used by AirPlay Receiver (ControlCenter). The tests use port 3456 instead. If you need to change the port, update:\n\n- `tests/e2e/playwright/playwright.config.ts` (both `baseURL` and `webServer.command/url`)\n\n### SPA routing not working\n\nThe e-commerce examples use History API routing (e.g., `/search/Appliances/`). The `website/serve.json` file configures rewrites for these routes. If you add new SPA routes, update this file:\n\n```json\n{\n  \"rewrites\": [\n    {\n      \"source\": \"/examples/js/e-commerce/search/**\",\n      \"destination\": \"/examples/js/e-commerce/index.html\"\n    }\n  ]\n}\n```\n\n### Tests are flaky with multiple workers\n\nThe static server can handle 2 parallel workers. All configs use 2 workers for both CI and local runs.\n\nThe main suite retries once locally and twice in CI. The Next.js suites retry twice in CI and not at all locally.\n\n### Multiple elements matched\n\nIf you see \"strict mode violation\" errors about multiple elements:\n\n- SearchBox selectors are scoped to the header to avoid matching RefinementList search inputs\n- Use more specific selectors when targeting widgets that may appear multiple times\n\n### Different HTML structures across flavors\n\nThe JS and React examples have different HTML structures:\n\n- **JS examples**: Use `id=\"header\"` and `data-widget=\"searchbox\"` attributes\n- **React examples**: Use `className=\"header\"` (class instead of id) and render SearchBox directly inside header\n\nThe fixtures account for this with combined selectors like:\n\n```typescript\n'#header .ais-SearchBox [type=search], .header > .ais-SearchBox [type=search], [data-widget=\"searchbox\"] .ais-SearchBox [type=search]';\n```\n\nIf you add new selectors, ensure they work across all flavors.\n\n## Migration Notes: WebDriverIO to Playwright\n\nWhen migrating tests from WebDriverIO to Playwright, be aware of these differences:\n\n### URL Waiting\n\n- **WebDriverIO**: Used custom `waitForUrl()` that polls `browser.getUrl()` until match\n- **Playwright**: Use `await expect(page).toHaveURL(url)` which has built-in retrying\n\n### Element Selection\n\n- **WebDriverIO**: `$('.ais-Hits-item')` returns first match, clicking works on container elements\n- **Playwright**: `page.locator('.ais-Hits-item').first()` - clicking on container may not hit child link elements. Use `.locator('a')` to target links explicitly:\n\n  ```typescript\n  // WebDriverIO\n  const link = await $('.ais-Hits-item');\n  await link.click();\n\n  // Playwright - click the link inside the hit\n  const link = page.locator('.ais-Hits-item a').first();\n  await link.click();\n  ```\n\n### Browser Back Navigation\n\n- **WebDriverIO**: `browser.back()` worked reliably for SPA navigation\n- **Playwright**: `page.goBack()` works but may require `slowMo` in headless mode for Next.js App Router dynamic routes\n\n### Headless Mode Timing Issues\n\nPlaywright's headless mode runs faster than headed mode, which can cause timing issues with SPA navigation and state updates. For Next.js App Router tests involving browser back/forward navigation with dynamic routes, add `slowMo` at the test file level (not globally, to avoid slowing down all tests):\n\n```typescript\nimport { test, expect } from '@playwright/test';\n\n// slowMo is required for browser back/forward navigation tests in headless mode\ntest.use({ launchOptions: { slowMo: 500 } });\n\ntest.describe('browser back/forward buttons', () => {\n  // tests that use page.goBack() or page.goForward()\n});\n```\n\nThis gives the browser enough time to process JavaScript events like popstate handlers used by InstantSearch routing, while keeping other tests fast.\n","frontmatter":{"paths":["tests/e2e/**",".github/workflows/e2e.yml",".github/workflows/ci.yml","website/serve.json","examples/*/e-commerce*/**","packages/react-instantsearch-nextjs/playwright.config.ts","packages/react-instantsearch-nextjs/__tests__/e2e/**","packages/react-instantsearch-router-nextjs/playwright.config.ts","packages/react-instantsearch-router-nextjs/__tests__/e2e/**"]},"isInternal":false,"tokens":2585,"sizeBytes":10474},{"name":"openai.yaml","path":".claude/skills/port-widget/agents/openai.yaml","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/.claude/skills/port-widget/agents/openai.yaml","title":"Agents Skill","category":"anthropic-skill","format":"yaml","content":"interface:\n  display_name: 'Port InstantSearch Widget'\n  short_description: 'Port widgets across InstantSearch flavors'\n  default_prompt: 'Use $port-widget to port a widget or connector-driven feature across the InstantSearch JavaScript, React, and Vue packages in this repo.'\n\npolicy:\n  allow_implicit_invocation: true\n","isInternal":false,"tokens":67,"sizeBytes":320},{"name":"js-flavor.md","path":".claude/skills/port-widget/references/js-flavor.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/.claude/skills/port-widget/references/js-flavor.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# JavaScript Flavor (`instantsearch.js`)\n\n## Reach for these precedents first\n\n- Shared UI via `instantsearch-ui-components`: `hits`, `related-products`, `trending-items`, `filter-suggestions`\n- Older Preact widget with templates and CSS helpers: `refinement-list`, `menu`, `pagination`\n\n## File layout\n\n```text\npackages/instantsearch.js/src/widgets/<widget>/\n  <widget>.tsx\n  defaultTemplates.tsx        # only when the widget exposes templates\n  __tests__/<widget>-test.tsx # optional flavor-specific tests\n```\n\n## Wrapper pattern\n\n- Import the connector from `../../connectors/<widget>/connect<Pascal>`.\n- Build a `createRenderer(...)` helper that closes over `containerNode`, classes, templates, and any normalized props.\n- Use `getContainerNode(container)` and throw via `createDocumentationMessageGenerator` when `container` is missing.\n- Mount with `render(<UiComponent ... />, containerNode)` and unmount with `render(null, containerNode)`.\n- Return `$$widgetType: 'ais.<camelName>'`.\n\n## UI choice\n\n- Prefer `instantsearch-ui-components` when the React and JS widgets should share markup.\n- Keep local JSX only when the JS widget still depends on legacy template plumbing or DOM structure that is not shared elsewhere.\n- If templates are supported, follow `TemplateComponent` and `prepareTemplateProps` patterns from a close sibling widget rather than inventing a new API.\n\n## Registration checklist\n\n- Export the connector from `packages/instantsearch.js/src/connectors/index.ts`.\n- Export the widget from `packages/instantsearch.js/src/widgets/index.ts`.\n","isInternal":false,"tokens":345,"sizeBytes":1566},{"name":"react-flavor.md","path":".claude/skills/port-widget/references/react-flavor.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/.claude/skills/port-widget/references/react-flavor.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# React Flavor (`react-instantsearch`)\n\n## Package split\n\n- `packages/react-instantsearch-core`: hooks that wrap `instantsearch.js` connectors\n- `packages/react-instantsearch`: public widgets and optional React-only UI components\n\n## Hook pattern\n\nFile: `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts`\n\n- Import the connector from `instantsearch.js/es/connectors/<widget>/connect<Pascal>`.\n- Import `useConnector` and `AdditionalWidgetProperties` from `../hooks/useConnector`.\n- Export `Use<Pascal>Props = <Pascal>ConnectorParams`.\n- Pass `additionalWidgetProperties` through to `useConnector(...)`.\n\n## Widget pattern\n\nChoose the closest precedent before writing code:\n\n- Shared UI component wrapper: `Hits.tsx`, `RelatedProducts.tsx`, `TrendingItems.tsx`, `FilterSuggestions.tsx`\n- React-only presentational UI in `src/ui`: `SearchBox.tsx`, `RangeInput.tsx`, `RefinementList.tsx`, `Menu.tsx`, `MenuSelect.tsx`, `SortBy.tsx`\n\n### If the UI is shared via `instantsearch-ui-components`\n\n- Instantiate the factory with `createElement as Pragma` and `Fragment`.\n- Keep connector params separate from UI-only props.\n- Pass `{ $$widgetType: 'ais.<camelName>' }` as the second argument to the hook.\n- Use `useInstantSearch()` only when the UI needs search status or other top-level state.\n- Only add `useMemo` or `useCallback` when an adjacent widget uses them to stabilize component props passed into a shared UI factory.\n\n### If no shared factory exists (React-only UI)\n\n- Create `packages/react-instantsearch/src/ui/<Pascal>.tsx` for the presentational component. This applies to all widgets without a shared factory — even simple ones like `MenuSelect` (a plain `<select>`).\n- Keep the widget file focused on mapping hook state and event handlers into the UI component.\n\n### Variant widgets (shared connector)\n\nSome widgets reuse another widget's hook instead of having a dedicated one. For example, `MenuSelect` uses `useMenu` (not `useMenuSelect`). In this case:\n\n- Import the existing hook directly in the widget file.\n- Pick only the relevant connector params (e.g., exclude `showMore`/`showMoreLimit` for `MenuSelect`).\n- Set a distinct `$$widgetType` (e.g., `'ais.menuSelect'`).\n\n## Registration checklist\n\n- Export the hook from `packages/react-instantsearch-core/src/index.ts` (skip for variant widgets that reuse an existing hook).\n- Export the widget from `packages/react-instantsearch/src/widgets/index.ts`.\n- If the widget is public, `packages/react-instantsearch/src/index.ts` already re-exports it through `./widgets`.\n- Add the widget to the switch in `packages/react-instantsearch/src/widgets/__tests__/__utils__/all-widgets.tsx` with required minimum props (e.g., `attribute=\"brand\"`).\n- In `packages/react-instantsearch/src/__tests__/common-widgets.test.tsx`:\n  - Replace the `throw new Error('X is not supported in React InstantSearch')` with real setup code.\n  - Remove the `skippedTests` entry for the widget in `testOptions`.\n","isInternal":false,"tokens":696,"sizeBytes":2966},{"name":"testing.md","path":".claude/skills/port-widget/references/testing.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/.claude/skills/port-widget/references/testing.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Testing and Examples\n\n## Start with the common suites\n\n### Wrapper or public widget change\n\n- Update `tests/common/widgets/<widget>/`.\n- Register or unskip the suite in each flavor's `common-widgets.test.*`:\n  - `packages/instantsearch.js/src/__tests__/common-widgets.test.tsx`\n  - `packages/react-instantsearch/src/__tests__/common-widgets.test.tsx`\n  - `packages/vue-instantsearch/src/__tests__/common-widgets.test.js`\n- For previously unsupported widgets: replace the `throw new Error('X is not supported in ...')` placeholder with real setup code, and remove the `skippedTests` entry in `testOptions`.\n\n### Connector or hook API change\n\n- Update `tests/common/connectors/<widget>/`.\n- Register or unskip the suite in:\n  - `packages/instantsearch.js/src/__tests__/common-connectors.test.tsx`\n  - `packages/react-instantsearch/src/__tests__/common-connectors.test.tsx`\n  - `packages/vue-instantsearch/src/__tests__/common-connectors.test.js`\n\n## Flavor-specific tests\n\n- JavaScript: `packages/instantsearch.js/src/widgets/<widget>/__tests__/`\n- React: `packages/react-instantsearch/src/widgets/__tests__/`\n- Vue: colocated component or package tests under `packages/vue-instantsearch/src/__tests__/`\n\nAdd flavor-specific tests only for behavior that the shared suites do not cover, such as:\n\n- slot rendering differences in Vue\n- React-only UI plumbing\n- template or CSS-class edge cases in `instantsearch.js`\n- registry coverage such as React's `all-widgets` helpers\n\n## Shared cross-widget suites\n\n- Search `tests/common/shared/` and `tests/common/shared-composition/` when the port affects routing, searchable behavior, or cross-widget composition.\n- Only touch those suites when the new widget changes shared behavior rather than a single widget contract.\n\n## Examples\n\n- Search existing examples before editing; not every user-facing widget belongs in the e-commerce demos.\n- E-commerce apps live in:\n  - `examples/js/e-commerce/src/`\n  - `examples/react/e-commerce/`\n  - `examples/vue/e-commerce/src/`\n- Recommendation, chat, and query-suggestion features already appear in getting-started or query-suggestions examples for JS and React.\n\n## Vue-specific test timing\n\n### State delivery is async for some connectors\n\nMost connectors (e.g. `connectRefinementList`) deliver state synchronously during `mountApp` because the mock search client responds immediately. Newer connectors like `connectChat` deliver state asynchronously. The Vue test setup must account for this:\n\n```javascript\nmountApp({ render: ... }, container);\nawait nextTick();                                      // Vue tick\nawait new Promise((resolve) => setTimeout(resolve, 0)); // macrotask for async state\nawait nextTick();                                      // Vue tick to re-render\n```\n\n### Provide a custom `act` for Vue\n\nVue batches DOM updates asynchronously, unlike React which re-renders synchronously within event handlers. Tests that `await act(async () => { await wait(0); })` work because `act` wraps the wait. Provide a custom `act` in `testOptions` that flushes Vue's update queue:\n\n```javascript\ncreateXxxWidgetTests: {\n  act: async (cb) => {\n    await cb();\n    await nextTick();\n  },\n},\n```\n\nTests that click then immediately assert DOM changes (without `act`) will still fail in Vue. These are known limitations of the shared test suite's React-centric timing assumptions. Document them in the `testOptions` but avoid skipping the entire test suite.\n\n### Flavored test params\n\nFor `flavored` test suites (like Chat), `runTestSuites` extracts the flavor-specific params via `widgetParams[flavor]`. The Vue setup function receives only the Vue portion. Ensure the Vue params include all required connector params (e.g. `agentId`, `chat`) — empty `{}` will cause connector errors.\n\nWhen Vue params need component functions (tools with `layoutComponent`, custom `headerComponent`), use `import { h as vueH } from 'vue'` in the `.tsx` test file and return VNodes: `() => vueH('div', { class: 'foo' }, 'text')`.\n\n### skippedTests granularity\n\n`skippableDescribe` only skips entire describe blocks by name. Individual test names in `skippedTests` have no effect unless the test file uses `skippableTest`. The shared chat/options/templates/translations test files use plain `test()`, so individual test skipping is not available for Chat widget tests.\n\n## Useful commands\n\n```bash\n# Common widget/connector tests\nnpx jest packages/instantsearch.js/src/__tests__/common-widgets.test.tsx --no-coverage\nnpx jest packages/instantsearch.js/src/__tests__/common-connectors.test.tsx --no-coverage\nnpx jest packages/react-instantsearch/src/__tests__/common-widgets.test.tsx --no-coverage\nnpx jest packages/react-instantsearch/src/__tests__/common-connectors.test.tsx --no-coverage\nnpx jest packages/vue-instantsearch/src/__tests__/common-widgets.test.js --no-coverage\n\n# Run only tests matching a specific widget name\nnpx jest packages/react-instantsearch/src/__tests__/common-widgets.test.tsx --testNamePattern=\"MenuSelect\" --no-coverage\n\n# Flavor-specific tests\nnpx jest packages/react-instantsearch/src/widgets/__tests__ --no-coverage\nnpx jest packages/instantsearch.js/src/widgets/<widget> --no-coverage\n\n# Update snapshots after adding new exports\nnpx jest packages/react-instantsearch/src/widgets/__tests__ --no-coverage -u\n\n# E2E\nyarn website:examples\nE2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n```\n","isInternal":false,"tokens":1233,"sizeBytes":5400},{"name":"vue-flavor.md","path":".claude/skills/port-widget/references/vue-flavor.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/.claude/skills/port-widget/references/vue-flavor.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Vue Flavor (`vue-instantsearch`)\n\n## Current patterns in this repo\n\n- Slot-heavy SFCs: `RefinementList.vue`, `Menu.vue`, `Pagination.vue`\n- Render-function wrappers around shared UI components: `Hits.js`, `Highlighter.js`\n- Framework glue helpers: `mixins/widget.js`, `mixins/suit.js`, `util/vue-compat.js`\n\n## Choose the wrapper shape deliberately\n\n### Use a `.vue` SFC when\n\n- the widget exposes multiple named slots\n- the DOM structure is easiest to express in template syntax\n- the closest existing widget is already an SFC\n\n### Use a `.js` render function when\n\n- the widget reuses `instantsearch-ui-components`\n- the wrapper mostly adapts connector state into a shared UI factory\n- the widget needs `renderCompat(...)` or lower-level vnode control\n\n## Implementation rules\n\n- Import connectors from `instantsearch.js/es/connectors/index`.\n- Use `createWidgetMixin({ connector: ... }, { $$widgetType: 'ais.<camelName>' })`.\n- Use `createSuitMixin({ name: '<Pascal>' })` for BEM classes. When the widget delegates all rendering to a shared `createXxxComponent` factory, the suit mixin's `suit()` method goes unused but the `classNames` prop it provides is still convenient. Pass `this.classNames` directly to the shared component's `classNames` prop (semantic keys like `{ root, container }`, not BEM keys).\n- Expose connector params through a computed `widgetParams()` object.\n- When reusing shared UI factories, wrap the render function with `renderCompat(...)` and map `this.classNames` into the `classNames` prop expected by the shared component.\n- Prefer `getScopedSlot` or `getDefaultSlot` helpers over direct slot access when matching render-function components.\n- In render-function callbacks that reference connector state (e.g. `onSubmit`, `onInput`), read from `this.state.xxx` instead of destructured locals. Vue batches re-renders, so destructured values become stale between synchronous user interactions (type then click).\n\n## Registration checklist\n\n- Export the component from `packages/vue-instantsearch/src/widgets.js`.\n- Do not manually edit `src/plugin.js` or `src/instantsearch.js` for normal widget additions; they already derive registration and exports from `widgets.js`.\n- Search `packages/vue-instantsearch/src/__tests__/common-widgets.test.js` and `common-connectors.test.js` for placeholder \"not supported\" branches before deciding the widget is genuinely absent.\n\n## Missing-feature caution\n\n- Recommendation widgets, chat, and filter suggestions already have connector-level test placeholders in Vue, but several still throw explicit \"not supported\" errors at the widget layer.\n- If you port one of those widgets, remove the placeholder failure and replace it with real setup code plus the component implementation.\n","isInternal":false,"tokens":597,"sizeBytes":2753},{"name":"SKILL.md","path":".claude/skills/port-widget/SKILL.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/.claude/skills/port-widget/SKILL.md","title":"port-widget","category":"anthropic-skill","format":"markdown","content":"---\nname: port-widget\ndescription: Port or introduce an InstantSearch widget or connector-driven feature across this monorepo's JavaScript, React, and Vue flavors. Use when the task is to add a missing wrapper around an existing connector, bring a widget from one flavor to another, or implement a new shared widget end-to-end in `instantsearch.js`, `react-instantsearch`, and `vue-instantsearch`. Triggers include requests like \"port X to vue\", \"add Y to react-instantsearch\", \"make Z available in all flavors\", or \"implement the missing InstantSearch wrapper for this connector\".\n---\n\n# Port InstantSearch Widgets Across Flavors\n\n## Start with the repo audit\n\n- Run `python3 scripts/audit_widget_coverage.py <widget-kebab-name>` from this skill folder before editing.\n- Use `--repo /path/to/instantsearch` if your current working directory is not inside the InstantSearch repo.\n- Treat placeholder Vue failures in `packages/vue-instantsearch/src/__tests__/common-widgets.test.js` or `common-connectors.test.js` as evidence that the connector exists but the Vue wrapper still needs work.\n\n## Layer map\n\n- Connector: `packages/instantsearch.js/src/connectors/<widget>/connect<Pascal>.ts`\n- JS widget: `packages/instantsearch.js/src/widgets/<widget>/<widget>.tsx`\n- React hook: `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts`\n- React widget: `packages/react-instantsearch/src/widgets/<Pascal>.tsx`\n- Optional React UI: `packages/react-instantsearch/src/ui/<Pascal>.tsx`\n- Vue wrapper: `packages/vue-instantsearch/src/components/<Pascal>.vue` or `.js`\n- Shared widget tests: `tests/common/widgets/<widget>/`\n- Shared connector tests: `tests/common/connectors/<widget>/`\n\n## Variant widgets\n\nSome widgets reuse another widget's connector with different defaults or UI. For example, `menuSelect` uses `connectMenu`/`useMenu` (not a dedicated `connectMenuSelect`). The audit will show `no` for connector and hook rows — this is expected. The `$$widgetType` still differs (`ais.menuSelect` vs `ais.menu`). When porting a variant widget, skip connector/hook creation and reuse the existing hook directly in the widget file.\n\nKnown variants: `menuSelect` → `connectMenu`/`useMenu`.\n\n## Workflow\n\n1. Decide the scope.\n   - Existing connector, missing wrapper: keep the connector API unchanged and port only the wrapper plus wrapper tests.\n   - Variant widget (shared connector, different UI): skip connector/hook creation; reuse the existing hook and set a distinct `$$widgetType`.\n   - Missing connector or changed render state: start in `instantsearch.js`, then update every flavor and both common test suites.\n   - Vue port for a newer recommendation, chat, or filter-suggestions feature: inspect `Hits.js`, `Highlighter.js`, `DynamicWidgets.js`, and `util/vue-compat.js` before designing the wrapper.\n2. Match a real precedent.\n   - Pick one close widget in the target flavor and one close widget in another flavor.\n   - Reuse the same prop names, slot or component escape hatches, `$$widgetType`, and test style.\n3. Build from the bottom up.\n   - Connector exports belong in `packages/instantsearch.js/src/connectors/index.ts`.\n   - JS widget exports belong in `packages/instantsearch.js/src/widgets/index.ts`.\n   - React hook exports belong in `packages/react-instantsearch-core/src/index.ts`.\n   - React widget exports belong in `packages/react-instantsearch/src/widgets/index.ts`; `packages/react-instantsearch/src/index.ts` already re-exports widgets.\n   - Vue exports belong in `packages/vue-instantsearch/src/widgets.js`; `src/instantsearch.js` and the plugin re-export and register from there automatically.\n4. Choose the right sharing model.\n   - JS and React: prefer `instantsearch-ui-components` when the markup can be shared.\n   - React: create `src/ui/<Pascal>.tsx` whenever the widget has no shared factory in `instantsearch-ui-components`. This includes simple widgets like `MenuSelect` (a plain `<select>`) — `src/ui/` is for all React-rendered markup, not only complex cases.\n   - Vue: use `.vue` SFCs for slot-heavy markup and `.js` render functions with `renderCompat` when reusing `instantsearch-ui-components`.\n5. Wire tests before finishing.\n   - Update `tests/common/widgets/<widget>/` whenever the wrapper behavior changes.\n   - Update `tests/common/connectors/<widget>/` whenever connector params or render state change.\n   - Register the suite in each flavor's `common-widgets.test.*` and `common-connectors.test.*`.\n   - Replace any `throw new Error('X is not supported in ...')` placeholder with real setup code in the target flavor's `common-widgets.test.*`.\n   - Remove the corresponding `skippedTests` entry in `testOptions` for that widget.\n   - For React: always add the widget to the switch in `packages/react-instantsearch/src/widgets/__tests__/__utils__/all-widgets.tsx` with the required minimum props.\n6. Check examples only when the widget is user-facing.\n   - Search existing examples first. Recommendation, chat, and query-suggestion widgets already live in getting-started or query-suggestions examples, not only the e-commerce apps.\n   - Add to `examples/*/e-commerce` only when the widget fits the shared storefront UX or existing Playwright coverage.\n\n## Reminders\n\n- Keep `$$widgetType` aligned across flavors.\n- Do not invent new Vue patterns; match `createWidgetMixin`, `createSuitMixin`, scoped slots, and `renderCompat`.\n- Do not add memoization hooks in React unless an adjacent widget uses them for the same reason.\n\n### Cross-package API migration checks\n\n- Update all affected layers in one pass when moving or splitting behavior: wrapper APIs, shared UI components, layout/type contracts, exports, and tests. Wrapper-only edits create cross-package type drift.\n- If you remove a required prop or render path, either delete it end-to-end or make downstream contracts optional in the same change; partial migrations often fail during declaration builds.\n- Before finishing, grep for legacy names (old props, options, class names, template keys, translations keys) to catch leftovers across flavors.\n- Keep control contracts explicit and consistent when behavior is externally controlled (e.g., imperative handles or widget methods), so external integrations do not depend on internal layout details.\n- Do not add ad-hoc top-level markdown change logs unless requested; keep source of truth in code, tests, and existing docs.\n- Always run monorepo build validation (`yarn build --ignore='example*'`) after cross-package API changes; it catches declaration and export drift that local tests may miss.\n\n## References\n\n- JS wrappers: [references/js-flavor.md](references/js-flavor.md)\n- React wrappers: [references/react-flavor.md](references/react-flavor.md)\n- Vue wrappers: [references/vue-flavor.md](references/vue-flavor.md)\n- Testing and examples: [references/testing.md](references/testing.md)\n","frontmatter":{"name":"port-widget","description":"Port or introduce an InstantSearch widget or connector-driven feature across this monorepo's JavaScript, React, and Vue flavors. Use when the task is to add a missing wrapper around an existing connector, bring a widget from one flavor to another, or implement a new shared widget end-to-end in `instantsearch.js`, `react-instantsearch`, and `vue-instantsearch`. Triggers include requests like \"port X to vue\", \"add Y to react-instantsearch\", \"make Z available in all flavors\", or \"implement the missing InstantSearch wrapper for this connector\"."},"isInternal":false,"tokens":1509,"sizeBytes":6868},{"name":"CLAUDE.md","path":"packages/algoliasearch-helper/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/algoliasearch-helper/CLAUDE.md","title":"Algoliasearch-helper Skill","category":"anthropic-skill","format":"markdown","content":"# algoliasearch-helper\n\nThe **low-level search-state manager** that sits _underneath_ InstantSearch connectors. It's a separately-published, long-stable npm package (`algoliasearch-helper`, own semver — currently 3.x) that wraps the `algoliasearch` JS client with a higher-level, stateful API: it tracks search parameters, builds the actual Algolia queries, parses responses, and emits events. Every connector in `instantsearch.js` reads and writes this helper's state — but the connector is the layer you change, not this one.\n\n> **⚠️ Rarely the right place for a change/fix/update.** This package is mature and broadly depended on — InstantSearch (all flavors) plus other Algolia front-end libraries build on it. Almost all widget/connector behavior is implemented _on top of_ the helper, not in it. Touch it **only** for a genuine state-manager or response-parsing bug, or to add a real new search-parameter primitive. Any change here is a **public-API, independently-versioned** change with a blast radius far beyond this repo — flag it explicitly and prefer fixing the connector instead.\n\n## What's in it\n\nEntry: `algoliasearchHelper(client, index, opts)` → an `AlgoliaSearchHelper` (an `EventEmitter`). Core pieces under `src/`:\n\n- **`SearchParameters/`** — the **immutable** query state (query, `facets`/`disjunctiveFacets`/`hierarchicalFacets`, numeric & tag refinements, `page`, etc.). Setters return a _new_ instance; never mutate in place. This is the object connectors mutate via `getWidgetSearchParameters`.\n- **`SearchResults/`** — parses the raw Algolia response into a richer structure: facet values, `facets_stats`, and hierarchical facet trees (`generate-hierarchical-tree.js`).\n- **`requestBuilder.js`** — turns `SearchParameters` into the actual query payload(s); notably **splits disjunctive faceting into multiple queries**.\n- **`DerivedHelper/`** — sub-requests derived from a main helper (federated / multi-index search); this is what powers the `index` widget.\n- **`RecommendParameters/` + `RecommendResults/`** — the equivalent state/results pair for the Recommend API.\n- **`functions/`** — small internal lodash-style utilities (`merge`, `omit`, `orderBy`, …).\n\nEvents emitted by the helper (what InstantSearch's lifecycle listens to): `change`, `search`, `result`, `error`, `fetch`, `searchOnce`, `searchForFacetValues`, `searchQueueEmpty`, `recommendQueueEmpty`.\n\n## Conventions that differ from the rest of the monorepo\n\n- **Plain CommonJS JavaScript**, not the TS-strict source the other packages use. Public types are **hand-written** in `index.d.ts` — update them by hand if you change the surface.\n- Has its own **`README.md` + `CHANGELOG.md`** (don't fold into the monorepo's). Build is Rollup (`yarn workspace algoliasearch-helper build`); tests run via its own `scripts/test.sh` (`yarn workspace algoliasearch-helper test`).\n","isInternal":false,"tokens":695,"sizeBytes":2892},{"name":"custom-templates.md","path":"packages/create-instantsearch-app/docs/custom-templates.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/docs/custom-templates.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# Creating a custom template\n\nBesides all the InstantSearch templates provided, the Create InstantSearch API enables you to create your own template.\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Defining the template](#defining-the-template)\n  - [`libraryName`](#libraryname)\n  - [`tasks`](#tasks)\n- [Injecting values to the template](#injecting-values-to-the-template)\n- [Using the template](#using-the-template)\n- [Examples](#examples)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## Defining the template\n\nOnce you've created a project directory, you can turn it into a template by adding a file `.template.js` at its root. This is the configuration file that drives `createInstantSearchApp()` to bootstrap your application.\n\nThis definition file returns a object with several properties.\n\n<h6 align=\"center\">.template.js</h6>\n\n```javascript\nconst { execSync } = require('child_process');\n\nmodule.exports = {\n  libraryName: 'react-instantsearch',\n  tasks: {\n    install(config) {\n      execSync(`cd ${config.path} && npm install`);\n    },\n    teardown(config) {\n      console.log('Begin by running: npm start');\n    },\n  },\n};\n```\n\n### `libraryName`\n\n> `string` (optional) | no default\n\nThe name of the [npm](https://www.npmjs.com) library to fetch versions for.\n\n### `tasks`\n\n> `object` | defaults as follows\n\nThe tasks are hooks that define your [application generation lifecycle](#lifecycle). Each of these hooks are functions which receives the [app configuration](#configuration-object) as parameters. You can use this configuration to drive the application generation.\n\n#### `setup(config: object)`\n\n> No default\n\nCan be leveraged to check the project requirements.\n\n#### `build(config: object)`\n\n> Defaults to copying and injecting the values\n\nCan be leveraged to generate the project differently.\n\n#### `install(config: object)`\n\n> No default\n\nCan be leveraged to install dependencies with a package manager.\n\n#### `clean(config: object)`\n\n> Defaults to deleting the project which installation has failed\n\nCan be leveraged to warn the user that the installation has failed.\n\n#### `teardown(config: object)`\n\n> No default\n\nCan be leveraged to display instructions to run the app.\n\n<h6 align=\"center\" id=\"lifecycle\">Application generation lifeycle</h6>\n\n![Setup → Build → Install → Clean (if installation fails) → Teardown](https://user-images.githubusercontent.com/6137112/41421858-f838c2a6-6ff7-11e8-8cef-4cc07f1f4f44.png)\n\n<h6 align=\"center\" id=\"configuration-object\"><code>config</code> object</h6>\n\n```js\n{\n  name: 'app-name',\n  path: '/dev/app-name',\n  template: 'InstantSearch.js',\n  libraryVersion: '2.9.0',\n  appId: 'APP_ID',\n  apiKey: 'API_KEY',\n  indexName: 'INDEX_NAME',\n  attributesToDisplay: ['title', 'description'],\n  installation: true,\n  silent: false,\n}\n```\n\n## Injecting values to the template\n\nBy default, the `build` task supports the [Handlebars](https://handlebarsjs.com) syntax to inject values passed to Create InstantSearch App. You can explicitly suffix the template files with `.hbs` or leave them as is (`.js` in this case).\n\n<h6 align=\"center\">App.js.hbs</h6>\n\n```handlebars\nimport React, { Component } from 'react';\nimport algoliasearch from 'algoliasearch/lite';\nimport {\n  InstantSearch,\n  Hits,\n  SearchBox,\n  {{#if attributesToDisplay}}\n  Highlight,\n  {{/if}}\n} from 'react-instantsearch-dom';\n\nconst searchClient = algoliasearch(\n  '{{appId}}',\n  '{{apiKey}}'\n);\n\n<InstantSearch searchClient={searchClient} indexName=\"{{indexName}}\">\n  <SearchBox placeholder=\"{{searchPlaceholder}}\" />\n  {/* ... */}\n</InstantSearch>\n```\n\n## Using the template\n\nCreate InstantSearch App reads your `.template.js` file and gets the specified tasks in the provided template.\n\nUsing the API:\n\n```javascript\nconst app = createInstantSearchApp('my-app', {\n  template: '/path/to/my-custom-template',\n});\n\napp.create();\n```\n\nUsing the CLI:\n\n```\ncreate-instantsearch-app my-app --template /path/to/my-custom-template\n```\n\n## Examples\n\nYou can find all the officially supported templates in the [`src/templates/`](../src/templates) folder.\n","isInternal":false,"tokens":1036,"sizeBytes":4283},{"name":"deploy.md","path":"packages/create-instantsearch-app/docs/deploy.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/docs/deploy.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# Deploy an InstantSearch app\n\nCreate InstantSearch App generates applications that are ready to be deployed. They rely on bundlers and framework toolkits (Parcel, Create React App, Vue CLI, etc.).\n\nYour app can be deployed in a single command, assuming you're located in its folder.\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [CodeSandbox](#codesandbox)\n- [Surge](#surge)\n- [Vercel](#vercel)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## CodeSandbox\n\n```\nnpx codesandbox .\n```\n\n[Learn more about CodeSandbox →](https://codesandbox.io)\n\n## Surge\n\n```\nnpx surge\n```\n\n[Learn more about Surge →](https://surge.sh)\n\n## Vercel\n\n```\nnpx vercel\n```\n\n[Learn more about Vercel →](https://vercel.com)\n","isInternal":false,"tokens":241,"sizeBytes":915},{"name":"CLAUDE.md","path":"packages/instantsearch-ui-components/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/instantsearch-ui-components/CLAUDE.md","title":"Instantsearch-ui-components Skill","category":"anthropic-skill","format":"markdown","content":"# instantsearch-ui-components\n\nThe **framework-agnostic UI layer** — the shared markup/layout for widgets, reused by every flavor. This is where a widget's DOM structure and `ais-*` CSS classes live for **newer** widgets. (Older widgets still define their layout per-flavor; see \"Old vs new\" below.)\n\n## The factory + renderer pattern\n\nComponents are **not** React/Preact/Vue components directly. Each is a factory that takes a renderer and returns a component:\n\n```tsx\n/** @jsx createElement */\nimport { cx } from '../lib';\nimport type { Renderer, ComponentProps } from '../types';\n\nexport function createHitsComponent({ createElement, Fragment }: Renderer) {\n  return function Hits(props) {\n    return <div className={cx('ais-Hits', props.classNames.root)}>…</div>;\n  };\n}\n```\n\n- **`Renderer` = `{ createElement, Fragment }`** is _injected_ by the consumer (`src/types/Renderer.ts`). Defaults are Preact's, but React passes `react`'s `createElement` and Vue passes its own `h` (wired up via the `renderCompat` helper in `vue-instantsearch`). **Never import `preact`/`react`/`vue` directly** — go through the injected renderer. The `/** @jsx createElement */` pragma at the top of each file is what wires JSX to the injected function.\n- Class names go through **`cx`** (`src/lib/cx.ts`) and follow the `ais-<Widget>-<element>` contract that `instantsearch.css` themes — treat those class names as a public API.\n- Props are typed with `ComponentProps<'div'>`-style helpers from `src/types`; expose a `classNames` partial so consumers can extend styling.\n\n## Layout\n\n- `src/components/<Name>.tsx` — a `create<Name>Component` factory. Exported from `src/components/index.ts` → `src/index.ts`.\n- Grouped families: `src/components/autocomplete/`, `src/components/chat/`, `src/components/recommend-shared/` (+ the recommend widgets `RelatedProducts`, `FrequentlyBoughtTogether`, `LookingSimilar`, `TrendingItems`, `TrendingFacets`).\n- `src/lib/` — `cx`, `stickToBottom`, shared `utils`. `src/types/` — `Renderer`, `ComponentProps`, `Hooks`, `Recommend`, `shared`.\n- Runtime deps are minimal on purpose (`@swc/helpers`); **no framework as a dependency**. Markdown rendering uses a vendored copy of `markdown-to-jsx` in `src/lib/markdown-to-jsx/` (React removed, see its README); it is linted, formatted and type-checked like the rest.\n\n## Old vs new (important)\n\n- **New widgets:** layout lives here and is consumed by all flavors — React via `import { create<Name>Component } from 'instantsearch-ui-components'`, Vue via the `renderCompat` helper, JS via the Preact renderer.\n- **Older widgets:** layout was defined per-flavor — JS in `packages/instantsearch.js/src/components/` (Preact), React in `packages/react-instantsearch/src/ui/`. The migration direction is to consolidate that markup here; some JS-package components are now thin wrappers that import from this package.\n- When adding/changing **shared** layout, do it here. When touching a widget that still defines its own markup, check whether it should be migrated rather than forked.\n\n## Working here\n\n```bash\nyarn workspace instantsearch-ui-components build       # tsc/rollup + build:types\nyarn jest packages/instantsearch-ui-components/src/components/__tests__/<Name>   # from repo root\n```\n\n- Tests are co-located in `src/components/__tests__/`. Test the component by passing a concrete renderer (e.g. Preact's) — assert rendered structure and class names, not implementation.\n- A markup or class-name change here **ripples to React, Vue, and JS at once** — verify consumers and keep the `ais-*` contract stable (a class rename is a breaking change that also touches `instantsearch.css`).\n","isInternal":false,"tokens":880,"sizeBytes":3674},{"name":"CLAUDE.md","path":"packages/instantsearch.js/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/instantsearch.js/CLAUDE.md","title":"Instantsearch.js Skill","category":"anthropic-skill","format":"markdown","content":"# instantsearch.js (core / vanilla JS)\n\nThe core package. **Every connector lives here and is consumed by all flavors** — React hooks and Vue components wrap these. A behavior change usually belongs in a connector here, not in a flavor package.\n\n## Layout\n\n- `src/connectors/<name>/connect<Pascal>.ts` — framework-agnostic logic. Signature: `connect<Widget>(renderFn, unmountFn)` returns a widget factory. Owns state, lifecycle, and search-param mutations. Exported from `src/connectors/index.ts`.\n- `src/widgets/<name>/<name>.tsx` — vanilla JS widget = connector + default template/rendering. Exported from `src/widgets/index.ts`.\n- `src/components/` — the **JS flavor's Preact components** (the widgets' markup). This is the _legacy_ home for widget layout; newer widgets pull their layout from the shared `instantsearch-ui-components` package instead, and some components here are now thin wrappers that import from it. Add **new shared** markup to `instantsearch-ui-components`, not here.\n- `src/lib/` — the InstantSearch runtime: lifecycle orchestration, routing, helper integration.\n- `src/lib/ai-lite/` — hand-written reimplementation of the Vercel AI SDK's UI layer (`UIMessage` parts, the SSE chunk protocol, the `Chat` state machine `connectChat` drives). It replaced `ai@^5.0.18` in #6880 and tracks nothing upstream: AI SDK fixes land on the `ai@6` line and never reach us. **Before changing its semantics, diff the matching file under [`vercel/ai/packages/ai/src/ui`](https://github.com/vercel/ai/tree/main/packages/ai/src/ui)** — the auto-continue predicate silently missed vercel/ai#9944 (don't resend provider-executed tool turns) for months. Deliberate gaps: no tool-approval flow, no metadata/data-part schema validation, no `finishReason`.\n- `src/types/` — public type definitions.\n\n## Working here\n\n```bash\nyarn workspace instantsearch.js build      # rollup → esm + cjs + umd + .d.ts\nyarn jest packages/instantsearch.js/src/connectors/<name>   # run one connector's tests (from repo root)\n```\n\n- Connectors return a typed render state — don't return untyped state (oxlint rule).\n- A new widget that only varies presentation should **reuse an existing connector** (e.g. `menuSelect` reuses `connectMenu`) and set a distinct `$$widgetType`; don't fork the connector.\n- Cross-flavor behavior is covered by `tests/common/connectors/<name>/`; flavor-agnostic logic tests go there, JS-specific ones stay in `__tests__/`.\n","isInternal":false,"tokens":592,"sizeBytes":2456},{"name":"CLAUDE.md","path":"packages/react-instantsearch-nextjs/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/react-instantsearch-nextjs/CLAUDE.md","title":"React-instantsearch-nextjs Skill","category":"anthropic-skill","format":"markdown","content":"# react-instantsearch-nextjs (Next.js App Router)\n\nSSR integration for the **App Router** (RSC + streaming). Peer-deps `next` + `react-instantsearch` — it **composes** react-instantsearch, never reimplements search. This is the non-trivial Next.js package; the subtle part is server-render + hydration.\n\n## Public surface\n\n- `InstantSearchNext` (`src/InstantSearchNext.tsx`) — the entry component, a drop-in for `<InstantSearch>` inside the App Router. It does SSR **for you**, so you don't hand-wire the plain-react-instantsearch `getServerState` + `InstantSearchSSRProvider` dance. Props: `routing` (boolean | object) and an optional `instance`.\n\n## How the SSR machinery fits together\n\n- `InitializePromise.ts` + `TriggerSearch.ts` — run the search **during the server (RSC) render** so results are available before HTML is sent.\n- `createInsertHTML.tsx` + `htmlEscape.ts` — serialize that initial server state and **inject it into the streamed HTML** for hydration. `htmlEscape` is a **safety boundary** (escapes the injected payload so it can't break out of the script/HTML context) — don't bypass it when touching the insert path.\n- `useInstantSearchRouting.ts`, `useNextHeaders.tsx`, `useDynamicRouteWarning.ts` — routing via `next/navigation` (not `next/router`). Dynamic routes are warned about (`useDynamicRouteWarning`) because they interact poorly with InstantSearch URL sync.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch-nextjs build\nyarn jest packages/react-instantsearch-nextjs/src/__tests__/<name>   # unit tests, from repo root\nyarn workspace react-instantsearch-nextjs test:e2e                   # Playwright (App Router)\n```\n\n- **Hydration is the failure mode.** A change to the initialize/trigger/insert path can produce server/client markup mismatch — verify there's no hydration warning and that the first paint already reflects server results.\n- e2e lives in `__tests__/e2e/` (Playwright). Browser back/forward on dynamic App Router routes needs file-level `slowMo` in headless mode — see `.claude/rules/e2e.md`.\n- Treat SSR/RSC/hydration work here as **reasoning-heavy** — it's the one corner of the React surface that isn't bounded widget wiring.\n","isInternal":false,"tokens":529,"sizeBytes":2206},{"name":"CLAUDE.md","path":"packages/react-instantsearch-router-nextjs/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/react-instantsearch-router-nextjs/CLAUDE.md","title":"React-instantsearch-router-nextjs Skill","category":"anthropic-skill","format":"markdown","content":"# react-instantsearch-router-nextjs (Next.js Pages Router)\n\nA **thin routing adapter** for the **Pages Router** — not a full SSR layer. It wraps InstantSearch's history router so URL sync plays well with `next/router`. The actual SSR is the standard react-instantsearch `getServerState` + `InstantSearchSSRProvider` pattern in your app, **not** here.\n\n## Public surface\n\n- `createInstantSearchRouterNext({ singletonRouter, serverUrl?, ... })` (`src/index.ts`) — returns a `Router` you pass to `<InstantSearch routing={{ router }}>`. Tagged `$$type: 'ais.nextjs'`.\n\n## What it actually does (and why)\n\n- Builds on `instantsearch.js/es/lib/routers/history`; on the server (`typeof window === 'undefined'`) it returns a simpler router reading from `serverUrl`.\n- `push` uses **`shallow: true`** so refinements don't re-run `getServerSideProps`, and strips the locale (`stripLocaleFromUrl`) to avoid Next i18n path errors.\n- Hooks `routeChangeComplete` + `beforePopState` to trigger SSR **only** on real page changes (back/forward to a different page), not on every refinement. `beforePopState`/`beforeStart`/`beforeDispose`/`routerOptions` let consumers compose their own logic.\n- `stripLocaleFromUrl` (`src/utils/`) handles i18n locale prefixes in the URL.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch-router-nextjs build\nyarn workspace react-instantsearch-router-nextjs test:e2e   # Playwright (Pages Router)\n```\n\n- Surface is small — most behavior lives in the shared history router (`instantsearch.js`) and react-instantsearch SSR core. Keep this package to the **Next-specific routing glue**.\n- The tricky cases are i18n/locale paths and back/forward navigation deciding whether to re-SSR. e2e in `__tests__/e2e/` (Playwright); see `.claude/rules/e2e.md`.\n","isInternal":false,"tokens":444,"sizeBytes":1780},{"name":"CLAUDE.md","path":"packages/react-instantsearch/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/react-instantsearch/CLAUDE.md","title":"React-instantsearch Skill","category":"anthropic-skill","format":"markdown","content":"# react-instantsearch\n\nReact DOM bindings. This package = **hooks (from `react-instantsearch-core`) + UI markup (from `instantsearch-ui-components`)**. The search logic itself is the connector in `instantsearch.js`; don't reimplement it here.\n\n## Layer split (where each thing goes)\n\n- `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts` — the **hook** that wraps the `instantsearch.js` connector. Headless, no DOM. Exported from that package's `src/index.ts`.\n- `packages/react-instantsearch/src/widgets/<Pascal>.tsx` — the **component**: calls the hook, renders UI. Exported from `src/widgets/index.ts`.\n- `packages/react-instantsearch/src/ui/<Pascal>.tsx` — local presentational component, only when the markup isn't already in the shared `instantsearch-ui-components` package.\n\nSo adding/changing a React widget often touches `react-instantsearch-core` (hook) **and** `react-instantsearch` (component) — keep them in sync.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch build\nyarn workspace react-instantsearch-core build\nyarn jest packages/react-instantsearch/src/widgets/__tests__/<Pascal>   # from repo root\n```\n\n- Uses React 19 / `@testing-library/react` for tests, co-located in `__tests__/`.\n- Cross-flavor behavior lives in `tests/common/widgets/<name>/`; register it in this package's `common-widgets.test.*`.\n- Prefer shared UI from `instantsearch-ui-components` (consume `create<Name>Component` with React's `createElement`) over new local `ui/` components. **Creating or changing a shared component is the `instantsearch-ui-components` package's job** — do that there, then consume it here. Add a local `src/ui/` component only when the markup truly isn't shareable.\n","isInternal":false,"tokens":412,"sizeBytes":1720},{"name":"CLAUDE.md","path":"packages/vue-instantsearch/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/vue-instantsearch/CLAUDE.md","title":"Vue-instantsearch Skill","category":"anthropic-skill","format":"markdown","content":"# vue-instantsearch\n\nVue 2 **and** Vue 3 bindings (single source, built for both). Components wrap the `instantsearch.js` connectors — the logic isn't reimplemented here.\n\n## Layout\n\n- `src/components/<Pascal>.vue` (SFC) or `.js` (render function) — the component wrapping a connector. Exported from `src/widgets.js`.\n- `src/mixins/` — shared connector-wiring logic reused across components.\n- To reuse shared markup, import the `create<Name>Component` factory from `instantsearch-ui-components` and render it inside a **`renderCompat`** render function, passing Vue's `h` as `createElement` — e.g. `render: renderCompat((h) => h(createHitsComponent({ createElement: h }), { … }))` (see `src/components/Hits.js`). `renderCompat` (`src/util/vue-compat/`) is the Vue 2/3 render-fn shim. **Creating or changing the shared component itself is the `instantsearch-ui-components` package's job** — do that there, then consume the factory here.\n\n## Working here\n\n```bash\nyarn workspace vue-instantsearch build\nyarn jest packages/vue-instantsearch/src/components/__tests__/<Pascal>   # from repo root\n```\n\n- Vue 2/3 dual support: run `./scripts/prepare-vue3.js` (root `yarn prepare-vue3`) when switching the working tree to Vue 3. Don't assume one Vue version — changes must work for both.\n- Cross-flavor behavior lives in `tests/common/widgets/<name>/`; register it in this package's `common-widgets.test.*`.\n","isInternal":false,"tokens":358,"sizeBytes":1415},{"name":"expose-option.md","path":".claude/commands/expose-option.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/.claude/commands/expose-option.md","title":"Command: /expose-option","category":"command-prompt","format":"markdown","content":"---\ndescription: Thread a new option on an existing connector through all three flavors (JS, React, Vue) + common tests\nargument-hint: <connector> <optionName> [short description of what it does]\n---\n\nYou are exposing a new option through the InstantSearch fan-out. The architecture is **connector (logic) → flavor wrapper → shared UI**, so the option is defined once in the connector and surfaced by each flavor.\n\n**Input:** `$ARGUMENTS`\n\n- First token = the connector (e.g. `connectRefinementList` or just `refinementList`).\n- Second token = the new option name (e.g. `clearOnChange`).\n- Remaining text = a short description of the behavior, if given.\n\nIf the connector or option name is missing or ambiguous, ask before changing files. If the behavior isn't specified, ask what the option should do — do **not** guess at semantics.\n\nThis is a checklist for a cross-flavor option rollout. The connector is the single source of truth; React and Vue only _wrap_ it, so do the connector first, then the wrappers.\n\n## Steps\n\n1. **Scope the contract first.** Read the connector `packages/instantsearch.js/src/connectors/<name>/connect<Pascal>.ts` and its JS widget `packages/instantsearch.js/src/widgets/<name>/<name>.tsx`. Confirm the option doesn't already exist and settle the exact semantics + the option's type signature. This is the contract every flavor must match.\n\n2. **Connector first** (in `packages/instantsearch.js`):\n   - Add the option to the connector's widget-params type with a TSDoc comment.\n   - Implement the behavior in the lifecycle (`init`/`render`/`getWidgetSearchParameters`/etc.).\n   - Surface it on the **typed render state** if consumers need it (untyped render state is an oxlint error).\n   - Thread it through the JS widget. Keep it framework-agnostic — no React/Vue/DOM-framework code in the connector.\n\n   For a presentational-only variant, **reuse the existing connector** with a distinct `$$widgetType` — never fork connector logic.\n\n3. **If the option changes shared markup/layout** (not just behavior), update the shared `create<Name>Component` in `packages/instantsearch-ui-components` _before_ wiring the flavors — its output is the contract they consume.\n\n4. **Thread it through the wrappers** (independent of each other once the connector is settled):\n   - **React** — the hook `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts` and the component `packages/react-instantsearch/src/widgets/<Pascal>.tsx`, kept in sync and typed.\n   - **Vue** — `packages/vue-instantsearch/src/components/<Pascal>.{vue,js}` (and `src/mixins/` if wiring lives there), working for **both Vue 2 and Vue 3**.\n\n5. **Tests.** Cross-flavor behavior → add/extend `tests/common/connectors/<name>/` (or `.../widgets/<name>/`) and register it in each flavor's `common-*.test.*` (see `tests/common/README.md`); flavor-specific assertions stay in each package's co-located `__tests__/`. If browser-level behavior is affected, add an e2e spec (see `.claude/rules/e2e.md`).\n\n6. **Verify.** Confirm all three flavors expose the same option with consistent naming/types, then:\n   ```bash\n   yarn jest packages/instantsearch.js/src/connectors/<name>\n   yarn jest <the react/vue paths you touched>\n   yarn type-check\n   yarn lint:changed\n   ```\n   (Or run `/preflight`.)\n\n## Notes\n\n- Why this order: the connector is the contract; React and Vue only _wrap_ it, so they're independent of each other and safe to do once the connector is settled.\n- Don't hand-edit changelogs (Ship.js generates them). Commit message: `feat(<widget>): add <optionName> option`.\n","frontmatter":{"description":"Thread a new option on an existing connector through all three flavors (JS, React, Vue) + common tests","argument-hint":"<connector> <optionName> [short description of what it does]"},"isInternal":false,"tokens":860,"sizeBytes":3591},{"name":"preflight.md","path":".claude/commands/preflight.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/.claude/commands/preflight.md","title":"Command: /preflight","category":"command-prompt","format":"markdown","content":"---\ndescription: Run the pre-push checklist — lint, types, relevant tests, and a Conventional-Commit sanity check\nallowed-tools: Bash(yarn lint:changed), Bash(yarn lint:fix), Bash(yarn type-check), Bash(yarn type-check:*), Bash(yarn jest:*), Bash(git status:*), Bash(git diff:*), Bash(git log:*)\n---\n\nRun the InstantSearch pre-push checks against the **current changes** and report a concise pass/fail summary. Don't fix anything silently — surface failures and propose fixes.\n\n## Steps\n\n1. **Scope the change.** `git status` + `git diff --name-only` (and vs. the base branch if on a feature branch) to see which packages/files are touched.\n\n2. **Lint the changed files:** `yarn lint:changed`. If it fails, show the violations; offer `yarn lint:fix` for auto-fixable ones (note that oxlint bans `for-in`/`for-of`/`async` and implicit `any` in library code — those need manual fixes).\n\n3. **Type-check:** `yarn type-check`. If any touched code interacts with the legacy algoliasearch versions, also run `yarn type-check:v3` / `yarn type-check:v4`.\n\n4. **Run the relevant tests** — not the whole suite. Map changed files to their `yarn jest <path>` (e.g. a connector → `yarn jest packages/instantsearch.js/src/connectors/<name>`; a React widget → its `__tests__` path). If a connector's cross-flavor behavior changed, include the matching `tests/common/` path.\n\n5. **Conventional-Commit sanity check.** Look at staged changes / recent commits and confirm the message fits `type(scope): description` (scope = widget/connector or topic like `deps`/`ci`). Flag if it doesn't; suggest a corrected message. Reference issues with `fix #1234` in the body when applicable.\n\n## Output\n\nReport a short checklist:\n\n- ✅/❌ lint:changed\n- ✅/❌ type-check (+ v3/v4 if run)\n- ✅/❌ tests (list the paths run)\n- ✅/❌ commit message\n\nFor any ❌, show the failing output and the smallest fix. Don't run the full `yarn test` or `yarn build` unless asked — this is the fast pre-push loop.\n\nRun the checks directly. If a check fails, fix it in the package that owns the failing file before re-running.\n","frontmatter":{"description":"Run the pre-push checklist — lint, types, relevant tests, and a Conventional-Commit sanity check","allowed-tools":"Bash(yarn lint:changed), Bash(yarn lint:fix), Bash(yarn type-check), Bash(yarn type-check:*), Bash(yarn jest:*), Bash(git status:*), Bash(git diff:*), Bash(git log:*)","allowedTools":["Bash(yarn","lint:changed),","Bash(yarn","lint:fix),","Bash(yarn","type-check),","Bash(yarn","type-check:*),","Bash(yarn","jest:*),","Bash(git","status:*),","Bash(git","diff:*),","Bash(git","log:*)"]},"allowedTools":["Bash(yarn","lint:changed),","Bash(yarn","lint:fix),","Bash(yarn","type-check),","Bash(yarn","type-check:*),","Bash(yarn","jest:*),","Bash(git","status:*),","Bash(git","diff:*),","Bash(git","log:*)"],"isInternal":false,"tokens":545,"sizeBytes":2105},{"name":"readme.md","path":"packages/algoliasearch-helper/examples/readme.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/algoliasearch-helper/examples/readme.md","title":"examples Documentation","category":"plugin-manifest","format":"markdown","content":"The examples moved to [codepen](http://codepen.io/collection/nwWJbe/).\n","isInternal":false,"tokens":22,"sizeBytes":71},{"name":"README.md","path":"packages/algoliasearch-helper/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/algoliasearch-helper/README.md","title":"algoliasearch-helper Documentation","category":"plugin-manifest","format":"markdown","content":"_Coming from V1 (or js client v2)?_ Read the [migration guide](https://github.com/algolia/algoliasearch-helper-js/wiki/Migration-guide-:-V1-to-V2) to the new version of the Helper.\n\n_Coming from V2?_ Read the [migration guide](https://github.com/algolia/algoliasearch-helper-js/wiki/Migration-guide-:-V2-to-V3) to the new version of the Helper.\n\n**The JavaScript helper is an advanced library we provide to our users. If you are looking to build a complete search interface, we recommend you to use [InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/). If you want to build an autocomplete menu, see [Autocomplete](https://github.com/algolia/autocomplete).**\n\n# algoliasearch-helper\n\nThis module is the companion of the [algolia/algoliasearch-client-javascript](https://github.com/algolia/algoliasearch-client-javascript). It helps you keep track of the search parameters and provides a higher level API.\n\n[See the helper in action](https://community.algolia.com/algoliasearch-helper-js/)\n\n[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Downloads][downloads-image]][downloads-url] [![jsDelivr Hits][jsdelivr-badge]][jsdelivr-hits]\n\n[license-image]: http://img.shields.io/badge/license-MIT-green.svg?style=flat-square\n[license-url]: LICENSE\n[downloads-image]: https://img.shields.io/npm/dm/algoliasearch-helper.svg?style=flat-square\n[downloads-url]: http://npm-stat.com/charts.html?package=algoliasearch-helper\n[version-svg]: https://img.shields.io/npm/v/algoliasearch-helper.svg?style=flat-square\n[package-url]: https://npmjs.org/package/algoliasearch-helper\n[jsdelivr-badge]: https://data.jsdelivr.com/v1/package/npm/algoliasearch-helper/badge\n[jsdelivr-hits]: https://www.jsdelivr.com/package/npm/algoliasearch-helper\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Features](#features)\n- [Examples](#examples)\n  - [Vanilla JavaScript](#vanilla-javascript)\n  - [AngularJS module](#angularjs-module)\n- [Helper cheatsheet](#helper-cheatsheet)\n  - [Add the helper in your project](#add-the-helper-in-your-project)\n  - [Regular `<script>` tag](#regular-script-tag)\n  - [With npm](#with-npm)\n  - [Init the helper](#init-the-helper)\n  - [Helper lifecycle](#helper-lifecycle)\n  - [Objects](#objects)\n  - [Search](#search)\n  - [Events](#events)\n  - [Query](#query)\n  - [Filtering results](#filtering-results)\n  - [Facet utilities](#facet-utilities)\n  - [Tags](#tags)\n  - [Pagination](#pagination)\n  - [Index](#index)\n  - [One time query](#one-time-query)\n  - [Query parameters](#query-parameters)\n  - [Results format](#results-format)\n- [Browser support](#browser-support)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Features\n\n- Search parameters management\n- Facets exclusions\n- Pagination\n- Disjunctive faceting (search on two or more values of the same facet)\n\n## Examples\n\n### Vanilla JavaScript\n\nA small example that uses Browserify to manage modules.\n\n```js\nvar algoliasearch = require('algoliasearch');\nvar algoliasearchHelper = require('algoliasearch-helper');\n\nvar client = algoliasearch('appId', 'apiKey');\n\nvar helper = algoliasearchHelper(client, 'indexName', {\n  facets: ['mainCharacterFirstName', 'year'],\n  disjunctiveFacets: ['director'],\n});\n\nhelper.on('result', function (event) {\n  console.log(event.results);\n});\n\nhelper.addDisjunctiveFacetRefinement('director', 'Clint Eastwood');\nhelper.addDisjunctiveFacetRefinement('director', 'Sofia Coppola');\n\nhelper.addNumericRefinement('year', '=', 2003);\n\n// Search for any movie filmed in 2003 and directed by either C. Eastwood or S. Coppola\nhelper.search();\n```\n\nSee more examples in the [examples folder](examples/)\n\n### AngularJS module\n\n```js\n<script src=\"https://cdn.jsdelivr.net/algoliasearch/3/algoliasearch.angular.js\"></script>\n<script src=\"dist/algoliasearch.helper.min.js\"></script>\n\n<script type=\"text/javascript\">\nangular.module('searchApp', ['ngSanitize', 'algoliasearch'])\n.controller('searchController', ['$scope', '$sce', 'algolia', function($scope, $sce, algolia) {\n  var algolia = algolia.Client('applicationId', 'apiKey');\n  $scope.q = '';\n  $scope.content = null;\n  $scope.helper = algoliasearchHelper(algolia, 'indexName', {\n    facets: ['type', 'shipping'],\n    disjunctiveFacets: ['category', 'manufacturer'],\n    hitsPerPage: 5,\n  });\n  $scope.helper.on('result', function(event) {\n    $scope.$apply(function() {\n      $scope.content = event.results;\n    });\n  });\n  $scope.toggleRefine = function($event, facet, value) {\n    $event.preventDefault();\n    $scope.helper.toggleRefine(facet, value).search();\n  };\n  $scope.$watch('q', function(q) {\n    $scope.helper.setQuery(q).search();\n  });\n  $scope.helper.search();\n}]);\n</script>\n```\n\nYou can see the full [Angular example here](https://codepen.io/Algolia/pen/PzZobK)\n\n## Helper cheatsheet\n\n[There is also a complete JSDoc](https://community.algolia.com/algoliasearch-helper-js/reference.html)\n\n### Add the helper in your project\n\n### Regular `<script>` tag\n\nUse our [jsDelivr](http://www.jsdelivr.com/) build:\n\n`<script src=\"https://cdn.jsdelivr.net/algoliasearch.helper/2/algoliasearch.helper.min.js\"></script>`\n\n### With npm\n\n`npm install algoliasearch-helper`\n\n### Init the helper\n\n```js\nvar helper = algoliasearchHelper(client, 'indexName' /*, parameters*/);\n```\n\n### Helper lifecycle\n\n1. modify the parameters of the search (usually through user interactions)<br/> `helper.setQuery('iphone').addFacetRefinement('category', 'phone')`\n\n2. trigger the search (after all the modification have been applied)<br/> `helper.search()`\n\n3. read the results (with the event \"result\" handler) and update the UI with the results<br/> `helper.on('result', function(event) { updateUI(event.results); });`\n\n4. go back to 1\n\n### Objects\n\n**AlgoliasearchHelper**: the helper. Keeps the state of the search, makes the queries and calls the handlers when an event happen.\n\n**SearchParameters**: the object representing the state of the search. The current state is stored in `helperInstance.state`.\n\n**SearchResults**: the object in which the Algolia answers are transformed into. This object is passed to the result event handler. An example of SearchResults in JSON is available at [the end of this readme](#results-format)\n\n### Search\n\nThe search is triggered by the `search()` method.\n\nIt takes all the previous modifications to the search and uses them to create the queries to Algolia. The search parameters are immutable.\n\nExample:\n\n```js\nvar helper = algoliasearchHelper(client, indexName);\n\n// Let's monitor the results with the console\nhelper.on('result', function (event) {\n  console.log(event.results);\n});\n\n// Let's make an empty search\n// The results are all sorted using the dashboard configuration\nhelper.search();\n\n// Let's search for \"landscape\"\nhelper.setQuery('landscape').search();\n\n// Let's add a category \"photo\"\n// Will make a search with \"photo\" tag and \"landscape\" as the query\nhelper.addTag('photo').search();\n```\n\n### Events\n\nThe helper is a Node.js [EventEmitter](https://nodejs.org/api/events.html#events_class_events_eventemitter) instance.\n\n`result`: get notified when new results are received. The handler function will receive two objects (`SearchResults` and `SearchParameters`).\n\n`error`: get notified when errors are received from the API.\n\n`change`: get notified when a property has changed in the helper\n\n`search` : get notified when a request is sent to Algolia\n\n#### Listen to the `result` event\n\n```js\nhelper.on('result', updateTheResults);\n```\n\n#### Listen to a `result` event once\n\n```js\nhelper.once('result', updateTheResults);\n```\n\n#### Remove a `result` listener\n\n```js\nhelper.removeListener('result', updateTheResults);\n```\n\n#### Remove all `result` listeners\n\n```js\nhelper.removeAllListeners('result');\n```\n\nAll the methods from Node.js [EventEmitter](https://nodejs.org/api/events.html#events_class_events_eventemitter) class are available.\n\n### Query\n\n#### Do a search with the query \"fruit\"\n\n```javscript\nhelper.setQuery('fruit').search();\n```\n\n### Filtering results\n\nFacets are filters to retrieve a subset of an index having a specific value for a given attribute. First you need to define which attribute will be used as a facet in the dashboard: [https://www.algolia.com/explorer#?tab=display](https://www.algolia.com/explorer#?tab=display)\n\n#### Regular (conjunctive) facets\n\nRefinements are ANDed by default (Conjunctive selection).\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  facets: ['ANDFacet'],\n});\n```\n\n##### Add a facet filter\n\n```js\nhelper.addFacetRefinement('ANDFacet', 'valueOfANDFacet').search();\n```\n\n##### Remove a facet filter\n\n```js\nhelper.removeFacetRefinement('ANDFacet', 'valueOfANDFacet').search();\n```\n\n#### Disjunctive facets\n\nRefinements are ORed by default (Disjunctive selection).\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  disjunctiveFacets: ['ORFacet'],\n});\n```\n\n##### Add a facet filter\n\n```js\nhelper.addDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();\n```\n\n##### Remove a facet filter\n\n```js\nhelper.removeDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();\n```\n\n#### Negative facets\n\nFilter so that we do NOT get a given facet\n\n##### Facet definition (same as \"AND\" facet)\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  facets: ['ANDFacet'],\n}).search();\n```\n\n##### Exclude a value for a facet\n\n```js\nhelper.addFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');\n```\n\n##### Remove an exclude from the list of excluded values\n\n```js\nhelper.removeFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');\n```\n\n#### Numeric facets\n\nFilter over numeric attributes with math operations like `=`, `>`, `<`, `>=`, `<=`. Can be used for numbers and dates (if converted to timestamp)\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  disjunctiveFacets: ['numericAttribute'],\n});\n```\n\n##### Add numeric refinements\n\n```js\nhelper.addNumericRefinement('numericAttribute', '=', '3').search();\n// filter to only the results that match numericAttribute=3\nhelper.addNumericRefinement('numericAttribute', '=', '4').search();\n// filter to only the results that match numericAttribute=3 AND numericAttribute=4\n\n// On another numeric with no previous filter\nhelper\n  .addNumericRefinement('numericAttribute2', '=', ['42', '56', '37'])\n  .search();\n// filter to only the results that match numericAttribute2=42 OR numericAttribute2=56 OR numericAttribute2=37\n```\n\n##### Remove a numeric refinement\n\n```js\nhelper.removeNumericRefinement('numericAttribute', '=', '3').search();\n```\n\n##### Batch numeric filter removal\n\n```js\n// for the single operator = on numericAttribute\nhelper.removeNumericRefinement('numericAttribute', '=').search();\n// for all the refinements on numericAttribute\nhelper.removeNumericRefinement('numericAttribute').search();\n```\n\n#### Hierarchical facets\n\nHierarchical facets are useful to build such navigation menus:\n\n```txt\n| products\n  > fruits\n    > citrus\n    | strawberries\n    | peaches\n    | apples\n```\n\nHere, we refined the search this way:\n\n- click on fruits\n- click on citrus\n\n##### Usage\n\nTo build such menu, you need to use hierarchical faceting:\n\n```javascript\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n    },\n  ],\n});\n```\n\n**Requirements:** All the specified `attributes` must be defined in your Algolia settings as attributes for faceting.\n\nGiven your objects looks like this:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": \"fruits\",\n    \"lvl1\": \"fruits > citrus\"\n  }\n}\n```\n\nAnd you refine `products`:\n\n```js\nhelper.toggleFacetRefinement('products', 'fruits > citrus');\n```\n\nYou will get a hierarchical presentation of your facet values: a navigation menu of your facet values.\n\n```js\nhelper.on('result', function (event) {\n  console.log(event.results.hierarchicalFacets[0]);\n  // {\n  //   'name': 'products',\n  //   'count': null,\n  //   'isRefined': true,\n  //   'path': null,\n  //   'data': [{\n  //     'name': 'fruits',\n  //     'path': 'fruits',\n  //     'count': 1,\n  //     'isRefined': true,\n  //     'data': [{\n  //       'name': 'citrus',\n  //       'path': 'fruits > citrus',\n  //       'count': 1,\n  //       'isRefined': true,\n  //       'data': null\n  //     }]\n  //   }]\n  // }\n});\n```\n\nTo ease navigation, we always:\n\n- provide the root level categories\n- provide the current refinement sub categories (`fruits > citrus > *`: n + 1)\n- provide the parent refinement (`fruits > citrus` => `fruits`: n -1) categories\n- refine the search using the current hierarchical refinement\n\n##### Multiple values per level\n\nYour records can also share multiple categories between one another by using arrays inside your object:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": [\"fruits\", \"color\"],\n    \"lvl1\": [\"fruits > citrus\", \"color > orange\"]\n  }\n},\n{\n  \"objectID\": \"456\",\n  \"name\": \"grapefruit\",\n  \"categories\": {\n    \"lvl0\": [\"fruits\", \"color\", \"new\"],\n    \"lvl1\": [\"fruits > citrus\", \"color > yellow\", \"new > citrus\"]\n  }\n}\n```\n\n##### Specifying another separator\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      separator: '|',\n    },\n  ],\n});\n\nhelper.toggleFacetRefinement('products', 'fruits|citrus');\n```\n\nWould mean that your objects look like so:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": \"fruits\",\n    \"lvl1\": \"fruits|citrus\"\n  }\n}\n```\n\n##### Specifying a different sort order for values\n\nThe default sort for the hierarchical facet view is: `isRefined:desc (first show refined), name:asc (then sort by name)`.\n\nYou can specify a different sort order by using:\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      sortBy: ['count:desc', 'name:asc'], // first show the most common values, then sort by name\n    },\n  ],\n});\n```\n\nThe available sort tokens are:\n\n- count\n- isRefined\n- name\n- path\n\n##### Restrict results and hierarchical values to non-root level\n\nLet's say you have a lot of levels:\n\n```\n- fruits\n  - yellow\n    - citrus\n      - spicy\n```\n\nBut you only want to get the values starting at \"citrus\", you can use `rootPath`\n\nYou can specify an root path to filter the hierarchical values\n\n```\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [{\n    name: 'products',\n    attributes: ['categories.lvl0', 'categories.lvl1', 'categories.lvl2', 'categories.lvl3'],\n    rootPath: 'fruits > yellow > citrus'\n  }]\n});\n```\n\nHaving a rootPath will refine the results on it **automatically**.\n\n##### Hide parent level of current parent level\n\nBy default the hierarchical facet is going to return the child and parent facet values of the current refinement.\n\nIf you do not want to get the parent facet values you can set showParentLevel to false\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      showParentLevel: false,\n    },\n  ],\n});\n```\n\n##### Asking for the current breadcrumb\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      separator: '|',\n    },\n  ],\n});\n\nhelper.toggleFacetRefinement('products', 'fruits|citrus');\nvar breadcrumb = helper.getHierarchicalFacetBreadcrumb('products');\n\nconsole.log(breadcrumb);\n// ['fruits', 'citrus']\n\nconsole.log(breadcrumb.join(' | '));\n// 'fruits | citrus'\n```\n\n#### Clearing filters\n\n##### Clear all the refinements for all the refined attributes\n\n```js\nhelper.clearRefinements().search();\n```\n\n##### Clear all the refinements for a specific attribute\n\n```js\nhelper.clearRefinements('ANDFacet').search();\n```\n\n##### [ADVANCED] Clear only the exclusions on the \"ANDFacet\" attribute\n\n```js\nhelper\n  .clearRefinements(function (value, attribute, type) {\n    return type === 'exclude' && attribute === 'ANDFacet';\n  })\n  .search();\n```\n\n### Facet utilities\n\n#### Get the values of a facet with the default sort\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetValues('age');\n  // It will be ordered :\n  //  - refined facets first\n  //  - then ordered by number of occurence (bigger count -> higher in the list)\n  //  - then ordered by name (alphabetically)\n});\n```\n\n#### Get the values of a facet with a custom sort\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetValues('age', { sortBy: ['count:asc'] });\n  // It will be ordered by number of occurence (lower number => higher position)\n  // Elements that can be sorted : count, name, isRefined\n  // Type of sort : 'asc' for ascending order, 'desc' for descending order\n});\n```\n\n#### Get the facet stats\n\n_This only apply on numeric based facets/attributes._\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetStats('age');\n});\n```\n\n### Tags\n\nTags are an easy way to do filtering. They are based on a special attribute in the records named `_tags`, which can be a single string value or an array of strings.\n\n#### Add a tag filter for the value \"landscape\"\n\n```js\nhelper.addTag('landscape').search();\n```\n\n#### Remove a tag filter for the value \"landscape\"\n\n```js\nhelper.removeTag('landscape').search();\n```\n\n#### Clear all the tags filters\n\n```js\nhelper.clearTags().search();\n```\n\n### Pagination\n\n#### Get the current page\n\n```js\nhelper.getPage();\n```\n\n#### Change page\n\n```js\nhelper.setPage(3).search();\n```\n\n#### Automatic reset to page 0\n\nDuring a search, changing the parameters will update the result set, which can then change the number of pages in the result set. Therefore, the behavior has been standardized so that any operation that may change the number of page will reset the pagination to page 0.\n\nThis may lead to some unexpected behavior. For example:\n\n```js\nhelper.setPage(4);\nhelper.getPage(); // 4\nhelper.setQuery('foo');\nhelper.getPage(); // 0\n```\n\nNon exhaustive list of operations that trigger a reset:\n\n- refinements (conjunctive, exclude, disjunctive, hierarchical, numeric)\n- tags\n- index (setIndex)\n- setQuery\n- setHitsPerPage\n- setTypoTolerance\n\n### Index\n\nIndex can be changed. The common use case is when you have several slaves with different sort order (sort by relevance, price or any other attribute).\n\n#### Change the current index\n\n```js\nhelper.setIndex('index_orderByPrice').search();\n```\n\n#### Get the current index\n\n```js\nvar currentIndex = helper.getIndex();\n```\n\n### One time query\n\nSometime it's convenient to reuse the current search parameters with small changes without changing the state stored in the helper. That's why there is a function called `searchOnce`. This method does not trigger `change` or `error` events.\n\nIn the following, we are using `searchOnce` to fetch only a single element using all the other parameters already set in the search parameters.\n\n#### Using searchOnce with a callback\n\n```js\nvar state = helper.searchOnce(\n  { hitsPerPage: 1 },\n  function (error, content, state) {\n    // if an error occured it will be passed in error, otherwise its value is null\n    // content contains the results formatted as a SearchResults\n    // state is the instance of SearchParameters used for this search\n  }\n);\n```\n\n#### Using searchOnce with a promise\n\n```js\nvar state1 = helper.searchOnce({ hitsPerPage: 1 }).then(function (res) {\n  // res contains\n  // {\n  //   content : SearchResults\n  //   state : SearchParameters (the one used for this specific search)\n  // }\n});\n```\n\n### Query parameters\n\nThere are lots of other parameters you can set.\n\n#### Set a parameter at the initialization of the helper\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hitsPerPage: 50,\n});\n```\n\n#### Set a parameter later\n\n```js\nhelper.setQueryParameter('hitsPerPage', 20).search();\n```\n\n#### List of parameters that can be set\n\n<table cellspacing=\"0\" cellpadding=\"0\" class=\"params\">\n  <tbody>\n    <tr>\n      <td valign=\"top\" class=\"td1\">\n        <p class=\"p1\"><span class=\"s1\"><b>Name</b></span></p>\n      </td>\n      <td valign=\"top\" class=\"td2\">\n        <p class=\"p1\"><span class=\"s1\"><b>Type</b></span></p>\n      </td>\n      <td valign=\"top\" class=\"td3\">\n        <p class=\"p1\"><span class=\"s1\"><b>Description</b></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">advancedSyntax</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the advanced syntax.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/advancedSyntax/\">advancedSyntax on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">allowTyposOnNumericTokens</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the engine allow typos on numerics.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/allowTyposOnNumericTokens/\">allowTyposOnNumericTokens on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">analytics</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the analytics</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/analytics/\">analytics on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">analyticsTags</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Tag of the query in the analytics.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/analyticsTags/\">analyticsTags on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundLatLng</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Center of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLng/\">aroundLatLng on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundLatLngViaIP</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Center of the search, retrieve from the user IP.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLngViaIP/\">aroundLatLngViaIP on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundPrecision</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Precision of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundPrecision/\">aroundPrecision on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundRadius</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Radius of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundRadius/\">aroundRadius on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minimumAroundRadius</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Minimum radius of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minimumAroundRadius/\">minimumAroundRadius on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToHighlight</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to highlight</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToHighlight/\">attributesToHighlight on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToRetrieve</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to retrieve</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToRetrieve/\">attributesToRetrieve on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToSnippet</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to snippet</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToSnippet/\">attributesToSnippet on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">disjunctiveFacets</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">Array.&lt;string&gt;</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">All the declared disjunctive facets</span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">distinct</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean|number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Remove duplicates based on the index setting attributeForDistinct</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/distinct/\">distinct on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">facets</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">Array.&lt;string&gt;</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">All the facets that will be requested to the server</span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">filters</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">Add filters to the query (similar to WHERE clauses)</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/filters/\">filters on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">getRankingInfo</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">integer</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the ranking informations in the response</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/getRankingInfo/\">getRankingInfo on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">hitsPerPage</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of hits to be returned by the search API</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/hitsPerPage/\">hitsPerPage on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">ignorePlurals</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the plurals be ignored</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/ignorePlurals/\">ignorePlurals on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">insideBoundingBox</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Geo search inside a box.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/insideBoundingBox/\">insideBoundingBox on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">insidePolygon</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Geo search inside a polygon.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/insidePolygon/\">insidePolygon on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">maxValuesPerFacet</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of values for each facetted attribute</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/maxValuesPerFacet/\">maxValuesPerFacet on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minWordSizefor1Typo</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of characters to wait before doing one character replacement.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minWordSizefor1Typo/\">minWordSizefor1Typo on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minWordSizefor2Typos</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of characters to wait before doing a second character replacement.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minWordSizefor2Typos/\">minWordSizefor2Typos on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">optionalWords</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Add some optional words to those defined in the dashboard</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/optionalWords/\">optionalWords on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">page</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">The current page number</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"algolia.com/doc/api-reference/api-parameters/page/\">page on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">query</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Query string of the instant search. The empty string is a valid query.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/query/\">query on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">queryType</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">How the query should be treated by the search engine. Possible values: prefixAll, prefixLast, prefixNone</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/queryType/\">queryType on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">removeWordsIfNoResults</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Possible values are \"lastWords\" \"firstWords\" \"allOptional\" \"none\" (default)</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/removeWordsIfNoResults/\">removeWordsIfNoResults on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">replaceSynonymsInHighlight</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the engine replace the synonyms in the highlighted results.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/replaceSynonymsInHighlight/\">replaceSynonymsInHighlight on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">restrictSearchableAttributes</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Restrict which attribute is searched.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/restrictSearchableAttributes/\">restrictSearchableAttributes on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">synonyms</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the synonyms</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/synonyms/\">synonyms on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">tagFilters</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Contains the tag filters in the raw format of the Algolia API. Setting this parameter is not compatible with the of the add/remove/toggle methods of the tag api.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/tagFilters/\">tagFilters on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">typoTolerance</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">How the typo tolerance behave in the search engine. Possible values: true, false, min, strict</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/typoTolerance/\">typoTolerance on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n  </tbody>\n</table>\n\n### Results format\n\nHere is an example of a result object you get with the `result` event.\n\n```js\n{\n   \"hitsPerPage\": 10,\n   \"processingTimeMS\": 2,\n   \"facets\": [\n      {\n         \"name\": \"type\",\n         \"data\": {\n            \"HardGood\": 6627,\n            \"BlackTie\": 550,\n            \"Music\": 665,\n            \"Software\": 131,\n            \"Game\": 456,\n            \"Movie\": 1571\n         },\n         \"exhaustive\": false\n      },\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"Free shipping\": 5507\n         },\n         \"name\": \"shipping\"\n      }\n   ],\n   \"hits\": [\n      {\n         \"thumbnailImage\": \"http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_54x108_s.gif\",\n         \"_highlightResult\": {\n            \"shortDescription\": {\n               \"matchLevel\": \"none\",\n               \"value\": \"Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection\",\n               \"matchedWords\": []\n            },\n            \"category\": {\n               \"matchLevel\": \"none\",\n               \"value\": \"Computer Security Software\",\n               \"matchedWords\": []\n            },\n            \"manufacturer\": {\n               \"matchedWords\": [],\n               \"value\": \"Webroot\",\n               \"matchLevel\": \"none\"\n            },\n            \"name\": {\n               \"value\": \"Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows\",\n               \"matchedWords\": [],\n               \"matchLevel\": \"none\"\n            }\n         },\n         \"image\": \"http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_105x210_sc.jpg\",\n         \"shipping\": \"Free shipping\",\n         \"bestSellingRank\": 4,\n         \"shortDescription\": \"Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection\",\n         \"url\": \"http://www.bestbuy.com/site/webroot-secureanywhere-internet-security-3-devi…d=1219060687969&skuId=1688832&cmp=RMX&ky=2d3GfEmNIzjA0vkzveHdZEBgpPCyMnLTJ\",\n         \"name\": \"Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows\",\n         \"category\": \"Computer Security Software\",\n         \"salePrice_range\": \"1 - 50\",\n         \"objectID\": \"1688832\",\n         \"type\": \"Software\",\n         \"customerReviewCount\": 5980,\n         \"salePrice\": 49.99,\n         \"manufacturer\": \"Webroot\"\n      },\n      ....\n   ],\n   \"nbHits\": 10000,\n   \"disjunctiveFacets\": [\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"5\": 183,\n            \"12\": 112,\n            \"7\": 149,\n            ...\n         },\n         \"name\": \"customerReviewCount\",\n         \"stats\": {\n            \"max\": 7461,\n            \"avg\": 157.939,\n            \"min\": 1\n         }\n      },\n      {\n         \"data\": {\n            \"Printer Ink\": 142,\n            \"Wireless Speakers\": 60,\n            \"Point & Shoot Cameras\": 48,\n            ...\n         },\n         \"name\": \"category\",\n         \"exhaustive\": false\n      },\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"> 5000\": 2,\n            \"1 - 50\": 6524,\n            \"501 - 2000\": 566,\n            \"201 - 500\": 1501,\n            \"101 - 200\": 1360,\n            \"2001 - 5000\": 47\n         },\n         \"name\": \"salePrice_range\"\n      },\n      {\n         \"data\": {\n            \"Dynex™\": 202,\n            \"Insignia™\": 230,\n            \"PNY\": 72,\n            ...\n         },\n         \"name\": \"manufacturer\",\n         \"exhaustive\": false\n      }\n   ],\n   \"query\": \"\",\n   \"nbPages\": 100,\n   \"page\": 0,\n   \"index\": \"bestbuy\"\n}\n```\n\n## Browser support\n\nThis project works on any [ES5](https://en.wikipedia.org/wiki/ECMAScript#5th_Edition) browser, basically >= IE9+.\n","isInternal":false,"tokens":12414,"sizeBytes":43884},{"name":"README.md","path":"packages/create-instantsearch-app/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/README.md","title":"create-instantsearch-app Documentation","category":"plugin-manifest","format":"markdown","content":"# Create InstantSearch App\n\n> ⚡️ Build InstantSearch apps at the speed of thought.\n\n[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Build Status][ci-svg]][ci-url]\n\n`create-instantsearch-app` is a command line utility that helps you quick start your InstantSearch app using any [Algolia][algolia-website] InstantSearch flavor ([InstantSearch.js][instantsearchjs-github], [React InstantSearch][react-instantsearch-github], [Vue InstantSearch][vue-instantsearch-github], [InstantSearch iOS][instantsearch-ios-github] and [InstantSearch Android][instantsearch-android-github]).\n\n<p align=\"center\">\n  <img src=\"preview.png\" width=\"800\" alt=\"Preview\">\n</p>\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Get started](#get-started)\n- [Usage](#usage)\n- [API](#api)\n- [Tutorials](#tutorials)\n- [Previews](#previews)\n- [Contributing](#contributing)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## Get started\n\n> The tool requires Node ≥ 8.\n\n```\nnpx create-instantsearch-app my-app\ncd my-app\nnpm start\n```\n\n> [`npx`](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) is a tool introduced in `npm@5.2.0` that makes it possible to run CLI tools hosted on the npm registry.\n\n---\n\nAlternatively, you can use [Yarn](https://yarnpkg.com):\n\n```\nyarn create instantsearch-app my-app\ncd my-app\nyarn start\n```\n\n> **Important** Selecting 'Y' when being prompted to ‘Enable user events’ activates the [`insights`](https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/#widget-param-insights) option for compatible templates, allowing Algolia to process your user Events. Events can unlock powerful features, enhancing your application's effectiveness and we encourage you to consider enabling this valuable functionality. Please review our [API reference](https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/#widget-param-insights) for more details about Events collection and settings.\n\n## Usage\n\nThis package comes with the module `createInstantSearchApp(path, options?)` and the command-line tool `create-instantsearch-app`.\n\n```\n$ create-instantsearch-app --help\n\n  Usage: create-instantsearch-app <project-directory> [options]\n\n  Options:\n\n    -v, --version                                      output the version number\n    --name <name>                                      The name of the application\n    --app-id <appId>                                   The application ID\n    --api-key <apiKey>                                 The Algolia search API key\n    --index-name <indexName>                           The main index of your search\n    --attributes-to-display <attributesToDisplay>      The attributes of your index to display\n    --image-attribute <imageAttribute>                 The attribute of your index to use for image display\n    --attributes-for-faceting <attributesForFaceting>  The attributes for faceting\n    --template <template>                              The InstantSearch template to use\n    --library-version <libraryVersion>                 The version of the library\n    --config <config>                                  The configuration file to get the options from\n    --no-installation                                  Ignore dependency installation\n    --no-interactive                                   Do not ask any interactive questions\n    -h, --help                                         output usage information\n```\n\n#### `--template`\n\nSupported templates are:\n\n- [`InstantSearch.js`][instantsearchjs-github]\n- [`React InstantSearch`][react-instantsearch-github]\n- [`React InstantSearch Native`][react-instantsearch-github]\n- [`Vue InstantSearch`][vue-instantsearch-github]\n- [`InstantSearch iOS`][instantsearch-ios-github]\n- [`InstantSearch Android`][instantsearch-android-github]\n\n```\ncreate-instantsearch-app my-app --template \"React InstantSearch\"\n```\n\nYou can also [create your own template](docs/custom-templates.md) and specify its path.\n\n#### `--config`\n\nThe `config` flag is handy to automate app generations.\n\n<h6 align=\"center\">config.json</h6>\n\n```json\n{\n  \"name\": \"my-app\",\n  \"template\": \"InstantSearch.js\",\n  \"libraryVersion\": \"2.8.0\",\n  \"appId\": \"MY_APP_ID\",\n  \"apiKey\": \"MY_API_KEY\",\n  \"indexName\": \"MY_INDEX_NAME\",\n  \"searchPlaceholder\": \"Search\",\n  \"attributesToDisplay\": [\"name\", \"description\"],\n  \"imageAttribute\": \"image\",\n  \"attributesForFaceting\": [\"brand\", \"location\"],\n  \"enableInsights\": true\n}\n```\n\nCreate the app based on this configuration:\n\n```\ncreate-instantsearch-app my-app --config config.json\n```\n\n## API\n\n`create-instantsearch-app` is based on the module `createInstantSearchApp(path, options?)`. The same [camel cased](https://en.wikipedia.org/wiki/Camel_case) options as the CLI are available.\n\n```javascript\nconst createInstantSearchApp = require('create-instantsearch-app');\n\nconst app = createInstantSearchApp('~/lab/my-app', {\n  template: 'InstantSearch.js',\n  libraryVersion: '2.0.0',\n  attributesToDisplay: ['name', 'description'],\n  imageAttribute: 'image',\n  attributesForFaceting: ['keywords'],\n  enableInsights: true,\n});\n\napp.create().then(() => console.log('App generated!'));\n```\n\n## Tutorials\n\n- [Creating a custom template](docs/custom-templates.md)\n- [Deploying an InstantSearch App](docs/deploy.md)\n\n## Previews\n\nYou can use the web templates on CodeSandbox:\n\n- [InstantSearch.js](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/instantsearch.js)\n- [React InstantSearch](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/react-instantsearch)\n- [Vue InstantSearch](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/vue-instantsearch)\n- [Autocomplete.js](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/autocomplete.js)\n- [JavaScript Client](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/javascript-client)\n- [JavaScript Helper](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/javascript-helper)\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to the code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nCreate InstantSearch App is [MIT licensed](LICENSE).\n\n<!-- Badges -->\n\n[version-svg]: https://img.shields.io/npm/v/create-instantsearch-app.svg?style=flat-square\n[package-url]: https://npmjs.org/package/create-instantsearch-app\n[ci-svg]: https://img.shields.io/circleci/project/github/algolia/create-instantsearch-app.svg?style=flat-square\n[ci-url]: https://circleci.com/gh/algolia/instantsearch\n[license-image]: https://img.shields.io/badge/license-MIT-green.svg?style=flat-square\n[license-url]: LICENSE\n\n<!-- Links -->\n\n[algolia-website]: https://www.algolia.com/?utm_medium=social-owned&utm_source=GitHub&utm_campaign=create-instantsearch-app%20repository\n[instantsearchjs-github]: https://github.com/algolia/instantsearch/tree/master/packages/instantsearch.js\n[react-instantsearch-github]: https://github.com/algolia/instantsearch/tree/master/packages/react-instantsearch\n[vue-instantsearch-github]: https://github.com/algolia/instantsearch/tree/master/packages/vue-instantsearch\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20Create+InstantSearch+App\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20Create+InstantSearch+App&title=Feature%20request%3A%20\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n","isInternal":false,"tokens":2263,"sizeBytes":9146},{"name":"README.md","path":"packages/create-instantsearch-app/src/templates/Autocomplete/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/src/templates/Autocomplete/README.md","title":"Autocomplete Documentation","category":"plugin-manifest","format":"markdown","content":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","isInternal":false,"tokens":121,"sizeBytes":463},{"name":"README.md","path":"packages/create-instantsearch-app/src/templates/InstantSearch.js widget/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/src/templates/InstantSearch.js widget/README.md","title":"InstantSearch.js widget Documentation","category":"plugin-manifest","format":"markdown","content":"# {{ name }}\n\n{{ description }}\n\n---\n\n[![MIT](https://img.shields.io/npm/l/{{ packageName }})](./LICENSE) [![NPM version](http://img.shields.io/npm/v/{{ packageName }}.svg)](https://npmjs.org/package/{{ packageName }})\n\n## Install\n\n```bash\nnpm install {{ packageName }}\n# or\nyarn add {{ packageName }}\n```\n\n## Widget\n\n### Usage\n\n```js\nimport instantsearch from 'instantsearch.js';\nimport algoliasearch from 'algoliasearch/lite';\nimport { {{ camelCaseName }} } from '{{ packageName }}';\n\nconst searchClient = algoliasearch('appId', 'apiKey');\n\nconst search = instantsearch({\n  indexName: 'indexName',\n  searchClient,\n});\n\nsearch.addWidgets([\n  {{ camelCaseName }}({\n    // widget parameters\n  }),\n]);\n\nsearch.start();\n```\n\n### Options\n\n| Option | Type | Required | Default | Description |\n| :-- | :-- | :-- | :-- | --- |\n| [`container`](#container) | `string` or `HTMLElement` | true | - | The element to insert the widget into. |\n| [`option1`](#option1) | `...` | true | - | REPLACE WITH THE DESCRIPTION FOR THIS OPTION |\n\n#### container\n\n> `string | Element` | **required**\n\nThe element to insert the widget into.\n\nThis can be either a valid CSS Selector:\n\n```js\n{{ camelCaseName }}({\n  container: '#{{ name }}',\n  // ...\n});\n```\n\nor an `HTMLElement`:\n\n```js\n{{ camelCaseName }}({\n  container: document.querySelector('#{{ name }}'),\n  // ...\n});\n```\n\n#### option1\n\n> `...` | **required**\n\nREPLACE WITH THE DESCRIPTION FOR THIS OPTION\n\n```js\n{{ camelCaseName }}({\n  option1: 'value',\n  // ...\n});\n```\n\n## Connector\n\n### Usage\n\n```js\nimport { connect{{ pascalCaseName }} } from '{{ packageName }}';\n\n// 1. Create a render function\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  // Rendering logic\n};\n\n// 2. Create the custom widget\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }}\n);\n\n// 3. Instantiate\nsearch.addWidgets([\n  custom{{ pascalCaseName }}({\n    // instance params\n  }),\n]);\n```\n\n### Options\n\n#### option1\n\n> `...`\n\nREPLACE WITH THE DESCRIPTION FOR THIS RENDERING ITEM\n\n```js\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  // show how to use this render option\n};\n\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }},\n);\n\nsearch.addWidgets([\n  custom{{pascalCaseName }}({\n    // ...\n  }),\n]);\n```\n\n#### widgetParams\n\n> `object`\n\nAll original widget options forwarded to the render function.\n\n```js\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  const { widgetParams } = renderOptions;\n  widgetParams.container.innerHTML = '...';\n};\n\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }},\n);\n\nsearch.addWidgets([\n  custom{{ pascalCaseName }}({\n    container: document.querySelector('#{{ name }}'),\n    // ...\n  }),\n]);\n```\n\n## Contributing\n\nTo start contributing to code, you need to:\n\n1. [Fork the project](https://docs.github.com/en/get-started/quickstart/fork-a-repo)\n2. [Clone the repository](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository)\n3. Install the dependencies: `yarn`\n4. Run the development mode: `yarn start`\n\nPlease read [our contribution process](./CONTRIBUTING.md) to learn more.\n\n---\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n","isInternal":false,"tokens":895,"sizeBytes":3484},{"name":"README.md","path":"packages/create-instantsearch-app/src/templates/InstantSearch.js/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/src/templates/InstantSearch.js/README.md","title":"InstantSearch.js Documentation","category":"plugin-manifest","format":"markdown","content":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","isInternal":false,"tokens":121,"sizeBytes":463},{"name":"README.md","path":"packages/create-instantsearch-app/src/templates/JavaScript Client/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/src/templates/JavaScript Client/README.md","title":"JavaScript Client Documentation","category":"plugin-manifest","format":"markdown","content":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","isInternal":false,"tokens":121,"sizeBytes":463},{"name":"README.md","path":"packages/create-instantsearch-app/src/templates/JavaScript Helper/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/src/templates/JavaScript Helper/README.md","title":"JavaScript Helper Documentation","category":"plugin-manifest","format":"markdown","content":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","isInternal":false,"tokens":121,"sizeBytes":463},{"name":"README.md","path":"packages/create-instantsearch-app/src/templates/React InstantSearch Native/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/src/templates/React InstantSearch Native/README.md","title":"React InstantSearch Native Documentation","category":"plugin-manifest","format":"markdown","content":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n","isInternal":false,"tokens":109,"sizeBytes":418},{"name":"README.md","path":"packages/create-instantsearch-app/src/templates/React InstantSearch/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/src/templates/React InstantSearch/README.md","title":"React InstantSearch Documentation","category":"plugin-manifest","format":"markdown","content":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n","isInternal":false,"tokens":109,"sizeBytes":418},{"name":"README.md","path":"packages/create-instantsearch-app/src/templates/Vue InstantSearch with Vue 3/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/src/templates/Vue InstantSearch with Vue 3/README.md","title":"Vue InstantSearch with Vue 3 Documentation","category":"plugin-manifest","format":"markdown","content":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","isInternal":false,"tokens":121,"sizeBytes":463},{"name":"README.md","path":"packages/create-instantsearch-app/src/templates/Vue InstantSearch/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/create-instantsearch-app/src/templates/Vue InstantSearch/README.md","title":"Vue InstantSearch Documentation","category":"plugin-manifest","format":"markdown","content":"# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n","isInternal":false,"tokens":121,"sizeBytes":463},{"name":"README.md","path":"packages/instantsearch-codemods/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/instantsearch-codemods/README.md","title":"instantsearch-codemods Documentation","category":"plugin-manifest","format":"markdown","content":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Codemods](#codemods)\n  - [`rish-to-ris`](#rish-to-ris)\n  - [`ris-v6-to-v7`](#ris-v6-to-v7)\n  - [`addWidget-to-addWidgets`](#addwidget-to-addwidgets)\n  - [Notes](#notes)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Codemods\n\nThese codemods (code transformers) can be ran with [jscodeshift](https://github.com/facebook/jscodeshift).\n\n### `rish-to-ris`\n\nThis will replace all imports from `react-instantsearch-hooks` to their `react-instantsearch` equivalent.\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#rish-to-ris' <path>\n```\n\n### `ris-v6-to-v7`\n\nThis will:\n\n- Replace `react-instantsearch-dom` imports to `react-instantsearch`\n- Replace prop names to their new equivalent\n- Replace `translations` keys to their new equivalent if they are defined inline, otherwise it will add a TODO comment\n- Add some TODO comments to help you migrate\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#ris-v6-to-v7' <path>\n```\n\n### `addWidget-to-addWidgets`\n\nThis will replace all `addWidget` calls to `addWidgets` and `removeWidget` calls to `removeWidgets`.\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#addWidget-to-addWidgets' <path>\n```\n\n### Notes\n\nIf you are using a formatter or a linter, make sure to run its autofixing after this transformation, since code can be formatted differently after it has been transformed. In our repository, that would be:\n\n```\nyarn format\n```\n","isInternal":false,"tokens":421,"sizeBytes":1594},{"name":"README.md","path":"packages/instantsearch-ui-components/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/instantsearch-ui-components/README.md","title":"instantsearch-ui-components Documentation","category":"plugin-manifest","format":"markdown","content":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [instantsearch-ui-components](#instantsearch-ui-components)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# instantsearch-ui-components\n\nInstantSearch UI Components is an open-source UI library used by InstantSearch to build UI for search and discovery interfaces. It exports UI components that are compatible with InstantSearch.js, React InstantSearch, and Vue InstantSearch.\n\n> Note: `instantsearch-ui-components` exists for internal usage and isn't designed for public consumption.\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nInstantSearch UI Components is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","isInternal":false,"tokens":748,"sizeBytes":2786},{"name":"README.md","path":"packages/instantsearch-ui-components/src/lib/markdown-to-jsx/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/instantsearch-ui-components/src/lib/markdown-to-jsx/README.md","title":"markdown-to-jsx Documentation","category":"plugin-manifest","format":"markdown","content":"Vendored copy of [`markdown-to-jsx`](https://github.com/quantizor/markdown-to-jsx) v7.7.15 (MIT, see `LICENSE`), taken from the original TypeScript embedded in the npm package's source map.\n\nChanges from upstream:\n\n- React types are replaced by the shared `Renderer` types, so the module has no `react` import, not even as a type.\n- `createElement` must be passed in options (no `React.createElement` fallback).\n- The `<Markdown>` React component and the default export are removed.\n- Raw HTML parsing is removed: the `htmlBlock` and `htmlSelfClosing` rules, the `disableParsingRawHTML` option and the state, types and helpers that only they used. HTML comments are still dropped.\n- Formatted and linted with the repo's config, and type-checked under its strict TypeScript config: non-null assertions, `any` casts for dynamic indexing, `null` added to a few declared types, unused parameters prefixed with `_`, `var` replaced by `let`/`const`, a sparse array literal written with an explicit `undefined`, `LOOKAHEAD` renamed to `lookahead`, type parameters renamed to the `T` prefix convention, and `eslint-disable` comments for the dev-only `console.warn` calls, the namespace and a deprecated type. The emitted JavaScript is otherwise unchanged.\n","isInternal":false,"tokens":287,"sizeBytes":1248},{"name":"README.md","path":"packages/instantsearch.css/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/instantsearch.css/README.md","title":"instantsearch.css Documentation","category":"plugin-manifest","format":"markdown","content":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Getting started](#getting-started)\n- [Installation](#installation)\n- [Available themes](#available-themes)\n- [Browser support](#browser-support)\n- [Implemented versions](#implemented-versions)\n- [Contributing](#contributing)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n<p align=\"center\">\n  <p align=\"center\">\n    InstantSearch.css is a CSS library to style InstantSearch-powered search experiences.\n  </p>\n</p>\n\n---\n\n[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Build Status][ci-svg]][ci-url] [![Website][website-svg]][website-url]\n\n## Getting started\n\nUsing InstantSearch.css is as simple as adding this CSS code to your existing InstantSearch app:\n\n```js\n// Include only the reset\nimport 'instantsearch.css/themes/reset.css';\n\n// Or include the full Satellite theme\nimport 'instantsearch.css/themes/satellite.css';\n```\n\nTo learn more about the library, follow the guide on how to style your InstantSearch widgets:\n\n- [InstantSearch.js](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/js/#style-your-widgets)\n- [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/react/#style-your-widgets)\n- [Vue InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/widgets/customize-an-existing-widget/vue/#style-your-widgets)\n\n## Installation\n\n```sh\nnpm install instantsearch.css\n# or\nyarn add instantsearch.css\n```\n\n## Available themes\n\nInstantSearch.css exposes two themes:\n\n- [Algolia](src/themes/algolia.scss)\n- [Satellite](src/themes/satellite.scss)\n\nIf you don't need a theme, you can still use the [reset styles](src/themes/reset.scss) to start from a clean slate.\n\n**The reset styles are included in all the provided themes.** You don't need to import them if you use a theme.\n\n## Browser support\n\nWe support the **last two versions of major browsers** (Chrome, Edge, Firefox, Safari).\n\n## Implemented versions\n\nInstantSearch.css is a living standard. This table tracks down the version implemented in each InstantSearch flavor.\n\n| Project             | Version |\n| ------------------- | ------- |\n| InstantSearch.js    | 7       |\n| React InstantSearch | 7       |\n| Vue InstantSearch   | 7       |\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nInstantSearch.css is [MIT licensed][license-url].\n\n<!-- Badges -->\n\n[version-svg]: https://img.shields.io/npm/v/instantsearch.css.svg?style=flat-square\n[package-url]: https://npmjs.org/package/instantsearch.css\n[ci-svg]: https://img.shields.io/circleci/project/github/algolia/instantsearch.svg?style=flat-square\n[ci-url]: https://circleci.com/gh/algolia/instantsearch\n[license-image]: http://img.shields.io/badge/license-MIT-green.svg?style=flat-square\n[website-svg]: https://img.shields.io/badge/website-instantsearchjs.netlify.app/specs-yellow?style=flat-square\n[website-url]: https://instantsearchjs.netlify.app/specs/\n[license-url]: LICENSE\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20InstantSearch.css\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20InstantSearch.css&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20InstantSearch.css\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20InstantSearch.css%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20InstantSearch.css%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20InstantSearch.css%22\n","isInternal":false,"tokens":1324,"sizeBytes":5092},{"name":"README.md","path":"packages/instantsearch.js/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/instantsearch.js/README.md","title":"instantsearch.js Documentation","category":"plugin-manifest","format":"markdown","content":"<p align=\"center\">\n  <a href=\"https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/\">\n    <img alt=\"InstantSearch.js\" src=\"https://github.com/algolia/instantsearch/blob/master/.github/banner.png?raw=true\">\n  </a>\n\n  <p align=\"center\">\n    InstantSearch.js is a JavaScript library for building performant and instant search experiences with <a href=\"https://www.algolia.com/?utm_source=instantsearch.js&utm_campaign=repository\">Algolia</a>.\n  </p>\n</p>\n\n---\n\nInstantSearch.js is a vanilla JavaScript library that lets you create an instant-search result experience using [Algolia][algolia-website]’s search API. It is part of the InstantSearch family:\n\n**InstantSearch.js** | [React InstantSearch][instantsearch-github] | [Vue InstantSearch][instantsearch-github] | [React InstantSearch Native][instantsearch-github] | [InstantSearch Android][instantsearch-android-github] | [InstantSearch iOS][instantsearch-ios-github]\n\n<details>\n  <summary><strong>Table of contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Why](#why)\n- [Getting started](#getting-started)\n- [Installation](#installation)\n  - [TypeScript users](#typescript-users)\n- [Documentation](#documentation)\n- [Demos](#demos)\n- [Playground](#playground)\n- [Browser support](#browser-support)\n- [Troubleshooting](#troubleshooting)\n- [Contributing](#contributing)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## Why\n\nYou should be using InstantSearch if you want to:\n\n- Design search experiences with best practices\n- Customize your components at will\n- Remain independent from external frameworks\n\n## Getting started\n\nUsing InstantSearch.js is as simple as adding this JavaScript code to your page:\n\n```javascript\n// 1. Instantiate the search\nconst search = instantsearch({\n  indexName: 'instant_search',\n  searchClient: algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76'),\n});\n\nsearch.addWidgets([\n  // 2. Create an interactive search box\n  instantsearch.widgets.searchBox({\n    container: '#searchbox',\n    placeholder: 'Search for products',\n  }),\n\n  // 3. Plug the search results into the product container\n  instantsearch.widgets.hits({\n    container: '#products',\n    templates: {\n      item: (hit, { html, components }) => html`\n        <h3>${components.Highlight({ attribute: 'name', hit })}</h3>\n        <p>${components.Snippet({ attribute: 'description', hit })}</p>\n      `,\n    },\n  }),\n\n  // 4. Make the brands refinable\n  instantsearch.widgets.refinementList({\n    container: '#brand',\n    attribute: 'brand',\n  }),\n]);\n\n// 5. Start the search!\nsearch.start();\n```\n\n<p align=\"center\">\n  <a href=\"https://codesandbox.io/s/github/algolia/doc-code-samples/tree/master/InstantSearch.js/getting-started\" title=\"Edit on CodeSandbox\">\n    <img alt=\"Edit on CodeSandbox\" src=\"https://codesandbox.io/static/img/play-codesandbox.svg\">\n  </a>\n</p>\n\nTo learn more about the library, follow the [getting started](https://www.algolia.com/doc/guides/building-search-ui/getting-started/js/) guide or check how to [add it to your own project](https://www.algolia.com/doc/guides/building-search-ui/installation/js/).\n\n## Installation\n\n```sh\nnpm install instantsearch.js algoliasearch\n# or\nyarn add instantsearch.js algoliasearch\n```\n\n### TypeScript users\n\nTo use InstantSearch.js in a TypeScript environment, depending on your [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) version, you need to import different types.\n\n> You still need to import these types even if you don't use InstantSearch.js with [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript).\n\n#### `algoliasearch` v4.x\n\nThis version uses types provided by both `algoliasearch` and `@algolia/client-search`.\n\n```bash\nyarn add algoliasearch@4 @algolia/client-search\n```\n\n#### `algoliasearch` v3.x\n\n```bash\nyarn add @types/algoliasearch@3\n```\n\n> v3.x is deprecated and will soon no longer be supported.\n\n## Documentation\n\nThe documentation is available on the [Algolia website](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/).\n\n## Demos\n\n| E-commerce | Media | Travel |\n| --- | --- | --- |\n| <a href=\"https://instantsearchjs.netlify.app/examples/e-commerce/\"><img src=\"https://www.algolia.com/doc/assets/images/build-search-ui/demos/e-commerce-2c7ed6b6.png\" width=\"250\" alt=\"E-commerce demo preview\"></a> | <a href=\"https://instantsearchjs.netlify.app/examples/media/\"><img src=\"https://www.algolia.com/doc/assets/images/build-search-ui/demos/media-articles-abc153ab.png\" width=\"250\" alt=\"Media demo preview\"></a> | <a href=\"https://instantsearchjs.netlify.app/examples/tourism/\"><img src=\"https://instantsearchjs.netlify.app/examples/tourism/capture.png\" width=\"250\" alt=\"Tourism demo preview\"></a> |\n\nSee more demos [on the website](https://www.algolia.com/doc/guides/building-search-ui/resources/demos/js/).\n\n## Playground\n\nYou can get to know InstantSearch.js on [this playground](https://codesandbox.io/s/github/algolia/create-instantsearch-app/tree/templates/instantsearch.js).\n\nStart by [adding widgets](https://www.algolia.com/doc/guides/building-search-ui/widgets/showcase/js/) and tweaking the display. Once you feel familiar with the library, we recommend following the [getting started guide](https://www.algolia.com/doc/guides/building-search-ui/getting-started/js/).\n\n## Browser support\n\nWe support the **last two versions of major browsers** (Chrome, Edge, Firefox, Safari).\n\nPlease refer to the [browser support](https://www.algolia.com/doc/guides/building-search-ui/installation/js/#browser-support) section in the documentation to use InstantSearch.js on other browsers.\n\n## Troubleshooting\n\nEncountering an issue? Before reaching out to support, we recommend heading to our [FAQ](https://www.algolia.com/doc/guides/building-search-ui/troubleshooting/faq/js/) where you will find answers for the most common issues and gotchas with the library.\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nInstantSearch.js is [MIT licensed][license-url].\n\n<!-- Links -->\n\n[license-url]: LICENSE\n[algolia-website]: https://www.algolia.com/?utm_source=instantsearch.js&utm_campaign=repository\n[instantsearch-github]: https://github.com/algolia/instantsearch/\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20InstantSearch.js\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20InstantSearch.js&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20InstantSearch.js\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20InstantSearch.js%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20InstantSearch.js%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20InstantSearch.js%22\n","isInternal":false,"tokens":2216,"sizeBytes":8470},{"name":"README.md","path":"packages/instantsearch.js/scripts/transforms/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/instantsearch.js/scripts/transforms/README.md","title":"transforms Documentation","category":"plugin-manifest","format":"markdown","content":"## Codemods\n\nThese codemods (code transformers) can be ran with [jscodeshift]((https://github.com/facebook/jscodeshift).\n\n### `addWidget-to-addWidgets`\n\nThis will replace calls of `addWidget(widget)` to `addWidgets([widget])`, as well as `removeWidget(widget) to `removeWidgets([widget])`.\n\n```\nnpx @codeshift/cli --packages 'instantsearch-codemods#addWidget-to-addWidgets' <path>\n```\n\n### Notes\n\nIf you are using a formatter or a linter, make sure to run its autofixing after this transformation, since code can be formatted differently after it has been transformed. In our repository, that would be:\n\n```\nyarn format\n```\n","isInternal":false,"tokens":154,"sizeBytes":624},{"name":"README.md","path":"packages/react-instantsearch-core/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/react-instantsearch-core/README.md","title":"react-instantsearch-core Documentation","category":"plugin-manifest","format":"markdown","content":"<p align=\"center\">\n  <a href=\"https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/\">\n    <img alt=\"React InstantSearch\" src=\"https://github.com/algolia/instantsearch/blob/master/.github/react-instantsearch-banner.png?raw=true\">\n  </a>\n</p>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch-core](#react-instantsearch-core)\n  - [Installation](#installation)\n  - [Getting started](#getting-started)\n  - [API reference](#api-reference)\n  - [Documentation](#documentation)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch-core\n\nReact InstantSearch Core is an open-source UI library for React that lets you quickly build a search interface in your front-end application.\n\nInstantSearch’s goal is to help you implement awesome search experiences as smoothly as possible by providing a [complete search ecosystem](https://algolia.com/doc/guides/getting-started/how-algolia-works/#the-full-ecosystem). InstantSearch tackles an important part of this vast goal by providing front-end primitives that you can assemble into unique search interfaces.\n\n<p align=\"center\">\n  <a href=\"https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/default-themes\" title=\"Edit on CodeSandbox\">\n    <img alt=\"Edit on CodeSandbox\" src=\"https://codesandbox.io/static/img/play-codesandbox.svg\">\n  </a>\n</p>\n\n> Note: `react-instantsearch-core` exports renderless components and hooks which can be used for both web and React Native. If you are using React in a web project, we recommend using the package `react-instantsearch` instead, as it includes complete components that render to the DOM.\n\n## Installation\n\nReact InstantSearch Core is available on the npm registry. It relies on [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) to communicate with Algolia APIs.\n\n```sh\nyarn add algoliasearch react-instantsearch-core\n# or\nnpm install algoliasearch react-instantsearch-core\n```\n\n## Getting started\n\nReact InstantSearch Core is a headless React library that lets you create an instant search results experience using Algolia’s search API.\n\nCheck out our [**Getting Started guide**](https://algolia.com/doc/guides/building-search-ui/getting-started/react/) to start implementing a full-featured search experience with React InstantSearch Core.\n\n## API reference\n\nCheck out the [**API reference**](https://www.algolia.com/doc/api-reference/widgets/react/).\n\n## Documentation\n\nThe documentation is available on [algolia.com/doc](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/).\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","isInternal":false,"tokens":1248,"sizeBytes":4855},{"name":"README.md","path":"packages/react-instantsearch-nextjs/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/react-instantsearch-nextjs/README.md","title":"react-instantsearch-nextjs Documentation","category":"plugin-manifest","format":"markdown","content":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch-nextjs](#react-instantsearch-nextjs)\n  - [Installation](#installation)\n  - [Usage](#usage)\n  - [API](#api)\n    - [`<InstantSearchNext>`](#instantsearchnext)\n    - [`routing` prop](#routing-prop)\n  - [Troubleshooting](#troubleshooting)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch-nextjs\n\nThis package provides server-side rendering for [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) that is compatible with [Next.js 13 App Router](https://nextjs.org/docs/app).\n\n## Installation\n\n```sh\nyarn add react-instantsearch-nextjs\n# or with npm\nnpm install react-instantsearch-nextjs\n```\n\n## Usage\n\n> [!NOTE] You can check this documentation on [Algolia's Documentation website](https://www.algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react/#app-router-experimental).\n\nYour search component must be in its own file, and it shouldn't be named `page.js` or `page.tsx`.\n\nTo render the component in the browser and allow users to interact with it, include the \"use client\" directive at the top of your code.\n\n```diff\n+'use client';\nimport { liteClient as algoliasearch } from 'algoliasearch/lite';\nimport {\n  InstantSearch,\n  SearchBox,\n} from 'react-instantsearch';\n\nconst searchClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');\n\nexport function Search() {\n  return (\n    <InstantSearch indexName=\"YourIndexName\" searchClient={searchClient}>\n      <SearchBox />\n      {/* other widgets */}\n    </InstantSearch>\n  );\n}\n```\n\nImport the `<InstantSearchNext>` component from the `react-instantsearch-nextjs` package, and replace the [`<InstantSearch>`](https://www.algolia.com/doc/api-reference/widgets/instantsearch/react/) component with it, without changing the props.\n\n```diff\n'use client';\nimport { liteClient as algoliasearch } from 'algoliasearch/lite';\nimport {\n- InstantSearch,\n  SearchBox,\n} from 'react-instantsearch';\n+import { InstantSearchNext } from 'react-instantsearch-nextjs';\n\nconst searchClient = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');\n\nexport function Search() {\n  return (\n-   <InstantSearch indexName=\"YourIndexName\" searchClient={searchClient}>\n+   <InstantSearchNext indexName=\"YourIndexName\" searchClient={searchClient}>\n      <SearchBox />\n      {/* other widgets */}\n\n-   </InstantSearch>\n+   </InstantSearchNext>\n  );\n}\n```\n\nTo serve your search page at `/search`, create an `app/search` directory. Inside it, create a `page.js` file (or `page.tsx` if you're using TypeScript).\n\nMake sure to [configure your route segment to be dynamic](https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic) so that Next.js generates a new page for each request.\n\n```jsx\n// app/search/page.js or app/search/page.tsx\nimport { Search } from './Search'; // change this with the path to your <Search> component\n\nexport const dynamic = 'force-dynamic';\n\nexport default function Page() {\n  return <Search />;\n}\n```\n\nYou can now visit `/search` to see your server-side rendered search page.\n\n## API\n\n### `<InstantSearchNext>`\n\nThe `<InstantSearchNext>` component is a drop-in replacement for the `<InstantSearch>` component. It accepts the same props, and it renders the same UI.\n\nYou can check the [InstantSearch API reference](https://www.algolia.com/doc/api-reference/widgets/instantsearch/react/) for more information.\n\n### `routing` prop\n\nAs with the `<InstantSearch>` component, you can pass a `routing` prop to the `<InstantSearchNext>` component to customize the routing behavior. The difference here is that `routing.router` takes [the same options as the `historyRouter`](https://www.algolia.com/doc/api-reference/widgets/history-router/react/).\n\n## Troubleshooting\n\nIf you're experiencing issues, please refer to the [**Need help?**](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/#need-help) section of the docs, or [open a new issue](https://github.com/algolia/instantsearch.js/issues/new?assignees=&labels=triage&template=BUG_REPORT.yml).\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","isInternal":false,"tokens":1649,"sizeBytes":6377},{"name":"README.md","path":"packages/react-instantsearch-router-nextjs/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/react-instantsearch-router-nextjs/README.md","title":"react-instantsearch-router-nextjs Documentation","category":"plugin-manifest","format":"markdown","content":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch-router-nextjs](#react-instantsearch-router-nextjs)\n  - [Installation](#installation)\n  - [Usage](#usage)\n  - [API](#api)\n  - [Troubleshooting](#troubleshooting)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch-router-nextjs\n\nThis package is a router for [React InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/) that is compatible with [Next.js](https://nextjs.org/) routing.\n\n> :warning: **This function cannot be used in conjunction with [`getStaticProps()`](https://nextjs.org/docs/api-reference/data-fetching/get-static-props). Use `getServerSideProps()` or client-side rendering instead.**\n\n## Installation\n\n```sh\nyarn add react-instantsearch-router-nextjs\n# or with npm\nnpm install react-instantsearch-router-nextjs\n```\n\n## Usage\n\nYou need to pass the Next.js router singleton that you can import from `next/router`.\n\nIf you are doing SSR with the `getServerState` and `InstantSearchSSRProvider` from `react-instantsearch/server`, you need to pass the `url` prop to `createInstantSearchRouterNext`'s `serverUrl` option :\n\n```js\nimport singletonRouter from 'next/router';\nimport { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';\n\nexport default function Page({ serverState, url }) {\n  return (\n    <InstantSearchSSRProvider {...serverState}>\n      <InstantSearch\n        searchClient={searchClient}\n        indexName=\"instant_search\"\n        routing={{\n          router: createInstantSearchRouterNext({\n            singletonRouter,\n            serverUrl: url,\n          }),\n        }}\n      >\n        {/* ... */}\n      </InstantSearch>\n    </InstantSearchSSRProvider>\n  );\n}\n```\n\nIf you are not doing SSR but only CSR, you can omit the `serverUrl` option:\n\n```js\nimport singletonRouter from 'next/router';\nimport { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';\n\nexport default function Page() {\n  return (\n    <InstantSearch\n      searchClient={searchClient}\n      indexName=\"instant_search\"\n      routing={{ router: createInstantSearchRouterNext({ singletonRouter }) }}\n    >\n      {/* ... */}\n    </InstantSearch>\n  );\n}\n```\n\nLastly, if you had custom routing logic in your app, you can pass it to the `createInstantSearchRouterNext`'s `routerOptions` option:\n\n```js\nimport singletonRouter from 'next/router';\nimport { createInstantSearchRouterNext } from 'react-instantsearch-router-nextjs';\n\nexport default function Page({ serverState, url }) {\n  return (\n    {/* ... */}\n      <InstantSearch\n        searchClient={searchClient}\n        indexName=\"instant_search\"\n        routing={{\n          router: createInstantSearchRouterNext({\n            singletonRouter,\n            serverUrl: url,\n            routerOptions: {\n              createURL: /* ... */,\n              parseURL: /* ... */,\n            },\n          }),\n           // if you are using a custom `stateMapping` you can still pass it :\n          stateMapping: /* ... */,\n        }}\n      >\n        {/* ... */}\n      </InstantSearch>\n    {/* ... */}\n  );\n}\n```\n\n## API\n\nThe options are :\n\n- `singletonRouter: SingletonRouter`: the required Next.js router singleton.\n- `serverUrl?: string`: the URL of the page on the server. Required if you are doing SSR with `getServerState` and `InstantSearchSSRProvider`.\n- `routerOptions?: RouterOptions`: the options passed to the `history` router. See [the documentation](https://www.algolia.com/doc/api-reference/widgets/history-router/js/) for more details.\n\nFor troubleshooting purposes, some other options are available :\n\n- `beforeStart?: (onUpdate: () => void) => void`: a function called before the router starts. You can use it to inform InstantSearch to update on router events by calling `onUpdate`.\n- `beforeDispose?: () => void`: a function called before the router disposes. You can use it to clean up handlers you added in `beforeStart`.\n- `beforePopState?: (options: { state: NextHistoryState, ownBeforePopState: BeforePopStateCallback, libraryBeforePopState: BeforePopStateCallback }) => boolean`: a function used by the Next.js router to know whether it should trigger SSR when using back/forward buttons. You can use it to override the default one by writing your own logic. The `ownBeforePopState` is the pre-existing handler that you may have set yourself, and the `libraryBeforePopState` is the default one from the library.\n\n## Troubleshooting\n\nIf you're experiencing issues, please refer to the [**Need help?**](https://algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/#need-help) section of the docs, or [open a new issue](https://github.com/algolia/instantsearch/issues/new?assignees=&labels=triage&template=BUG_REPORT.yml).\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","isInternal":false,"tokens":1738,"sizeBytes":7016},{"name":"README.md","path":"packages/react-instantsearch/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/react-instantsearch/README.md","title":"react-instantsearch Documentation","category":"plugin-manifest","format":"markdown","content":"<p align=\"center\">\n  <a href=\"https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/\">\n    <img alt=\"React InstantSearch\" src=\"https://github.com/algolia/instantsearch/blob/master/.github/react-instantsearch-banner.png?raw=true\">\n  </a>\n</p>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [react-instantsearch](#react-instantsearch)\n  - [Why](#why)\n  - [Installation](#installation)\n  - [Getting started](#getting-started)\n  - [Documentation](#documentation)\n  - [Playground](#playground)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# react-instantsearch\n\nReact InstantSearch is an open-source React library that lets you create an instant search result experience using [Algolia][algolia-website]’s search API. It is part of the InstantSearch family:\n\n**React InstantSearch** | [InstantSearch.js][instantsearch-github] | [Vue InstantSearch][instantsearch-github] | [InstantSearch Android][instantsearch-android-github] | [InstantSearch iOS][instantsearch-ios-github]\n\n## Why\n\nYou should be using React InstantSearch if you want to:\n\n- Design search experiences with best practices\n- Customize your components at will\n- Follow React principles\n\nNote: If you are working with React Native, or otherwise do not use the DOM, check out `react-instantsearch-core` instead.\n\n## Installation\n\nReact InstantSearch is available on the npm registry. It relies on [`algoliasearch`](https://github.com/algolia/algoliasearch-client-javascript) to communicate with Algolia APIs.\n\n```sh\nyarn add algoliasearch react-instantsearch\n# or\nnpm install algoliasearch react-instantsearch\n```\n\n## Getting started\n\nUsing React InstantSearch is as simple as adding these components to your app:\n\n```javascript\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { liteClient as algoliasearch } from 'algoliasearch/lite';\nimport { InstantSearch, SearchBox, Hits } from 'react-instantsearch';\n\nconst searchClient = algoliasearch(\n  'latency',\n  '6be0576ff61c053d5f9a3225e2a90f76'\n);\n\nconst App = () => (\n  <InstantSearch indexName=\"bestbuy\" searchClient={searchClient}>\n    <SearchBox />\n    <Hits />\n  </InstantSearch>\n);\n```\n\n<p align=\"center\">\n  <a href=\"https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/default-theme\" title=\"Edit on CodeSandbox\">\n    <img alt=\"Edit on CodeSandbox\" src=\"https://codesandbox.io/static/img/play-codesandbox.svg\">\n  </a>\n</p>\n\nTo learn more about the library, follow the [getting started guide][doc-getting-started].\n\n## Documentation\n\nThe documentation is available on [algolia.com/doc][doc].\n\n## Playground\n\nYou can get to know React InstantSearch on [this playground][doc-playground].\n\nStart by [adding components][doc-getting-started] and tweaking the display. Once you get more familiar with the library, you can learn more advanced concepts in [our guides][doc-guides].\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Documentation**. Did you find a typo in the doc? [Open an issue][contributing-newissue] and we'll take care of it.\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nReact InstantSearch is [MIT licensed](../../LICENSE).\n\n<!-- Links -->\n\n[doc]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/react/\n[doc-getting-started]: https://www.algolia.com/doc/guides/building-search-ui/getting-started/react/\n[doc-guides]: https://www.algolia.com/doc/guides/building-search-ui/going-further/server-side-rendering/react/\n[doc-playground]: https://codesandbox.io/s/github/algolia/instantsearch/tree/master/examples/react/default-theme\n[algolia-website]: https://www.algolia.com/\n[instantsearch-github]: https://github.com/algolia/instantsearch\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20React+InstantSearch\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20React+InstantSearch&title=Feature%20request%3A%20\n[contributing-newissue]: https://github.com/algolia/instantsearch/issues/new?labels=triage,Library%3A%20React+InstantSearch\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20React+InstantSearch%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20React+InstantSearch%22\n","isInternal":false,"tokens":1531,"sizeBytes":5787},{"name":"README.md","path":"packages/vue-instantsearch/README.md","rawUrl":"https://raw.githubusercontent.com/algolia/instantsearch/HEAD/packages/vue-instantsearch/README.md","title":"vue-instantsearch Documentation","category":"plugin-manifest","format":"markdown","content":"<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Vue InstantSearch](#vue-instantsearch)\n- [Documentation](#documentation)\n- [Installation](#installation)\n- [Troubleshooting](#troubleshooting)\n- [Contributing, dev, release](#contributing-dev-release)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n[![Vue InstantSearch logo][logo]][website]\n\nInstantSearch projects: **Vue InstantSearch** | [InstantSearch.js][instantsearch-github] | [React InstantSearch][instantsearch-github] | [InstantSearch Android][instantsearch-android-github] | [InstantSearch iOS][instantsearch-ios-github].\n\n## Vue InstantSearch\n\n> ⚡ Lightning-fast search for Vue.js apps\n\nBuilt by [Algolia][algolia-website].\n\nThis repository holds the code for the Vue InstantSearch project.\n\n## Documentation\n\nThere's a dedicated documentation available at [www.algolia.com/doc/guides/building-search-ui/getting-started/vue][website].\n\n## Installation\n\nVue InstantSearch is available in the npm registry. Install it:\n\n```sh\n# with npm\nnpm install --save vue-instantsearch\n\n# with yarn\nyarn add vue-instantsearch\n```\n\nTo learn more about the usage, follow our [getting started guide][getting-started-guide].\n\n## Troubleshooting\n\nEncountering an issue? Before reaching out to support, we recommend heading to our [FAQ](https://www.algolia.com/doc/guides/building-search-ui/troubleshooting/faq/vue/) where you will find answers for the most common issues and gotchas with the library.\n\n## Contributing, dev, release\n\nWe welcome all contributors, from casual to regular. You are only one command away to start the developer environment, [read our CONTRIBUTING guide](CONTRIBUTING.md).\n\n[logo]: vue-instantsearch-readme.png\n[website]: https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/vue/\n[getting-started-guide]: https://www.algolia.com/doc/guides/building-search-ui/getting-started/vue/\n[algolia-website]: https://www.algolia.com/\n[instantsearch-github]: https://github.com/algolia/instantsearch\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n","isInternal":false,"tokens":548,"sizeBytes":2274}],"systemPromptSnippet":"<yakaai_skills repo=\"algolia/instantsearch\">\n<!-- File: AGENTS.md (Tokens: ~4 | Category: root-instruction) -->\nCLAUDE.md\n\n<!-- File: CLAUDE.md (Tokens: ~1854 | Category: claude-rule) -->\n# InstantSearch monorepo\n\nSearch UI libraries for Algolia across three flavors (vanilla JS, React, Vue), sharing one connector + UI-component core. Yarn 4 workspaces + Lerna (independent versioning).\n\n## Repo map\n\n| Package | What it is |\n| --- | --- |\n| `packages/instantsearch.js` | **Core.** Vanilla JS library. **All connectors live here** (`src/connectors/`) and are shared by every flavor. |\n| `packages/react-instantsearch-core` | Headless React: hooks (`useSearchBox`, …) that wrap connectors. Framework-agnostic (web/RN). |\n| `packages/react-instantsearch` | React DOM widgets = hooks + UI from `instantsearch-ui-components`. |\n| `packages/vue-instantsearch` | Vue 2/3 components wrapping connectors. |\n| `packages/instantsearch-ui-components` | **Shared UI layer.** Framework-agnostic widget markup + `ais-*` classes via factory components (`create<Name>Component({ createElement, Fragment })`); reused by React directly, Vue via `renderCompat`, JS via the Preact renderer. Newer widgets get their layout here; older ones still define it in `instantsearch.js/src/components/`. |\n| `packages/algoliasearch-helper` | Low-level search-parameter/state manager underneath connectors. Mature, separately-versioned; **rarely the place for a change** — fix the connector instead. See its `CLAUDE.md`. |\n| `packages/instantsearch.css` | Default themes. |\n| `packages/react-instantsearch-nextjs` / `-router-nextjs` | Next.js App Router / Pages Router integrations. |\n| `packages/create-instantsearch-app`, `instantsearch-cli`, `instantsearch-codemods` | Scaffolding / CLI / migration tooling. |\n\nArchitecture in one line: **connector (logic, in instantsearch.js) → flavor wrapper (JS widget / React hook+component / Vue component) → shared UI components**. Change behavior in the connector; change markup in the UI layer.\n\n## Where work goes (per layer)\n\nBecause behavior lives in the connector and the flavors only wrap it, most changes have a natural home — and the per-package `CLAUDE.md` files carry each layer's conventions:\n\n| Work in… | Package |\n| --- | --- |\n| Connectors, JS widgets, runtime (`src/lib`), legacy Preact components (`src/components`), types | `packages/instantsearch.js` |\n| Shared framework-agnostic markup / `ais-*` classes | `packages/instantsearch-ui-components` |\n| React widgets/hooks + Next.js integrations | `react-instantsearch` / `react-instantsearch-core` / `*-nextjs` |\n| Vue components (Vue 2 **and** 3) | `vue-instantsearch` |\n\nFor a **cross-flavor change**, settle the contract in the connector first, then update the React and Vue wrappers, then the common tests. If the change is to **shared markup/layout** (not behavior), it lives in `instantsearch-ui-components` and the flavors consume the result — a class/structure change there ripples to all flavors and `instantsearch.css` at once.\n\n**Adding or surfacing a connector option is always cross-flavor work** — settle the contract in the connector first, then thread it through React **and** Vue, then add/extend common tests. Don't wire one flavor and stop. The **`/expose-option <connector> <option>`** command walks this recipe; **`/preflight`** runs the pre-push lint/types/tests/commit check; the `/port-widget` skill adds a brand-new widget across flavors.\n\n## Commands\n\nNode `20.19.0` (`.nvmrc`). Use Yarn (v4, pinned by `packageManager`).\n\n```bash\n# Build\nyarn build                      # all packages (lerna)\nyarn workspace <pkg> build      # single package, e.g. yarn workspace instantsearch.js build\n\n# Unit tests (Jest, jsdom)\nyarn jest <path-or-pattern>     # fastest loop: run one file/pattern at root\nyarn jest packages/<pkg>        # one package's suite (flavor pkgs have no `test` script — scope jest by path)\nyarn jest common-widgets        # shared cross-flavor widget suites (JS+React+Vue); -connectors for connectors\nyarn jest common-widgets -t \"Chat widget common tests\"   # scope to one widget (label = \"<Widget> widget common tests\")\nyarn test                       # everything (slow)\n\n# Lint (oxlint) + format (oxfmt) + types\nyarn lint:changed               # lint only changed files — use this while iterating\nyarn lint:fix                   # auto-fix\nyarn format                     # format the repo; yarn format:check only reports\nyarn type-check                 # tsc + per-package (also :v3 / :v4 for legacy algoliasearch)\n\n# E2E — see .claude/rules/e2e.md (Playwright). Examples must be built first:\nyarn website:examples && E2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n```\n\n## Conventions\n\n- **TypeScript strict.** No implicit any; unused vars/params error. Avoid `for-in`/`for-of` and `async` in library code (oxlint enforces).\n- **Tests** co-locate in `__tests__/` next to source — Jest picks up _any_ file under `__tests__/` (the default `testMatch`), so some legacy tests are plain `*.js`; name new ones `*.test.ts(x)`. Prefer focused assertions; use inline snapshots sparingly (initial render only). Mocks/helpers come from `@instantsearch/mocks` and `@instantsearch/testutils`.\n- **Cross-flavor tests** live in `tests/common/{widgets,connectors}/<name>/` and each flavor registers them in its `common-widgets.test.*` / `common-connectors.test.*`. Run with `yarn jest common-widgets` (all three flavors) and scope to one widget via `-t \"<Widget> widget common tests\"`. This is the primary test surface for newer widgets like Autocomplete and Chat. See `tests/common/README.md`.\n- **Commits: Conventional Commits** — `type(scope): description`, scope = widget/connector or topic (`deps`, `ci`). e.g. `fix(searchbox): increase magnifying glass size`, `feat(hits): add custom rendering`. Reference issues with `fix #1234` in the body.\n- **Branches:** target `master`; `vX` branches are critical-fix-only. Branch names: `fix/<issue>`, `feat/<name>`.\n- **Releases** are automated via Ship.js (`yarn release`); changelogs are generated from commits — don't hand-edit them.\n- **Agent workflows that write to another repo** (`.github/workflows/docs-*.yml`) must run with the working directory _inside_ that repo. A `CLAUDE.md`/`AGENTS.md` in a workspace subdirectory is never loaded, so the target repo's conventions get silently ignored — reach the source of this repo via `--add-dir` instead. Same for `--allowedTools`: `Bash(git diff:*)` matches, `Bash(git diff *)` matches nothing, and every denied call burns a turn.\n\n## Keep these docs alive\n\nIf you discover something durable and non-obvious while working — a gotcha that cost you a debugging detour, a convention, a non-obvious file location, a cross-flavor constraint — **propose** adding it to the right doc (package `CLAUDE.md` if package-specific, this file if repo-wide, `.claude/rules/e2e.md` for e2e). Surface the proposed edit for the user to approve rather than editing silently. Bar: it must generalize beyond the current task (same bar as a good code comment) and not already be documented.\n\n## Reference docs (read before relevant work — don't duplicate here)\n\n- `CONTRIBUTING.md` — full process, commit/branch rules, package-import flow.\n- `.claude/rules/e2e.md` — E2E testing (Playwright helpers, flavors, debugging).\n- `.claude/skills/port-widget/` — adding a widget across all three flavors (the `/port-widget` skill).\n- `tests/common/README.md` — shared cross-flavor test architecture.\n- Per-package `CLAUDE.md` in `instantsearch.js`, `instantsearch-ui-components`, `react-instantsearch`, `vue-instantsearch` for package-specific layout.\n\n<!-- File: .claude/rules/e2e.md (Tokens: ~2585 | Category: anthropic-skill) -->\n---\npaths:\n  - 'tests/e2e/**'\n  - '.github/workflows/e2e.yml'\n  - '.github/workflows/ci.yml'\n  - 'website/serve.json'\n  - 'examples/*/e-commerce*/**'\n  - 'packages/react-instantsearch-nextjs/playwright.config.ts'\n  - 'packages/react-instantsearch-nextjs/__tests__/e2e/**'\n  - 'packages/react-instantsearch-router-nextjs/playwright.config.ts'\n  - 'packages/react-instantsearch-router-nextjs/__tests__/e2e/**'\n---\n\n# E2E Testing Guide\n\n## Test Locations and Frameworks\n\n| Location | Framework | Purpose |\n| --- | --- | --- |\n| `tests/e2e/playwright/` | Playwright | Main e2e suite for all InstantSearch flavors |\n| `tests/e2e/` (wdio files) | WebdriverIO | IE11 tests only (via Sauce Labs) |\n| `packages/react-instantsearch-nextjs/__tests__/e2e/` | Playwright | App Router Next.js e2e tests |\n| `packages/react-instantsearch-router-nextjs/__tests__/e2e/` | Playwright | Pages Router Next.js e2e tests |\n\n`.github/workflows/e2e.yml` runs on a weekday schedule and is called from `ci.yml` on pushes to master, never on pull requests. Run the suites locally before you push.\n\n## Running Tests Locally\n\n### Main E2E Suite (Playwright)\n\n**Prerequisites:** Examples must be built first:\n\n```bash\nyarn website:examples\n```\n\n```bash\n# Run all Playwright tests (all flavors, chromium + firefox)\nyarn test:e2e\n\n# Run tests for a specific flavor\nE2E_FLAVOR=react yarn test:e2e\n\n# Run tests for a specific browser (faster local dev)\nE2E_BROWSER=chromium yarn test:e2e\n\n# Combine both for fastest local iteration\nE2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n\n# Run tests with UI mode for debugging\nyarn workspace @instantsearch/e2e-tests test:playwright --ui\n```\n\n### Next.js Package Tests\n\n```bash\n# App Router (react-instantsearch-nextjs)\nyarn workspace react-instantsearch-nextjs test:e2e\n\n# Pages Router (react-instantsearch-router-nextjs)\nyarn workspace react-instantsearch-router-nextjs test:e2e\n\n# Run with list reporter (outputs to terminal instead of opening browser)\nyarn workspace react-instantsearch-nextjs test:e2e --reporter=list\nyarn workspace react-instantsearch-router-nextjs test:e2e --reporter=list\n```\n\n### IE11 Tests (Requires Sauce Labs credentials)\n\n```bash\n# Set environment variables first\nexport SAUCE_USERNAME=your_username\nexport SAUCE_ACCESS_KEY=your_access_key\n\nyarn test:e2e:ie11\n```\n\n## Writing Playwright Tests\n\n### Test Structure\n\n```typescript\nimport { test, expect } from '../fixtures';\n\ntest.describe('feature name', () => {\n  test('does something specific', async ({ page, helpers }) => {\n    // Use '.' to navigate to baseURL, NOT '/' (which goes to root)\n    await page.goto('.');\n\n    // Use helpers for common InstantSearch interactions\n    await helpers.clickRefinementListItem('Apple');\n    await helpers.setSearchBoxValue('macbook');\n\n    // Use Playwright's built-in assertions\n    await expect(page.locator('.ais-Hits-item')).toHaveCount(16);\n  });\n});\n```\n\n### Available Helper Methods\n\nThe `helpers` fixture provides these methods. Full signatures in `tests/e2e/playwright/fixtures.ts`.\n\n- **RefinementList:** `clickRefinementListItem(label)`, `getSelectedRefinementListItem()`\n- **SearchBox:** `setSearchBoxValue(value)`, `getSearchBoxValue()`\n- **Hits:** `getHitsTitles()`\n- **HierarchicalMenu:** `clickHierarchicalMenuItem(label)`, `getSelectedHierarchicalMenuItems()`\n- **RangeSlider:** `dragRangeSliderLowerBoundTo(value)`, `dragRangeSliderUpperBoundTo(value)`, `getRangeSliderLowerBoundValue()`, `getRangeSliderUpperBoundValue()`\n- **Pagination:** `clickPage(number)`, `clickNextPage()`, `clickPreviousPage()`, `getCurrentPage()`\n- **ToggleRefinement:** `clickToggleRefinement()`, `getToggleRefinementStatus()`\n- **RatingMenu:** `clickRatingMenuItem(label)`, `getSelectedRatingMenuItem()`. `label` is the link's `aria-label`, such as `4 & up`.\n- **SortBy:** `setSortByValue(label)`, `getSortByValue()`\n- **HitsPerPage:** `setHitsPerPage(label)`, `getHitsPerPage()`\n- **ClearRefinements:** `clickClearRefinements()`\n- **Any selector:** `waitForElement(selector)`, `getTextFromSelector(selector)`\n\n### Best Practices\n\n1. **Use helpers for InstantSearch interactions** - They handle waiting for URL changes automatically\n2. **Prefer Playwright's auto-waiting assertions** - Use `await expect(locator).toHaveText('...')` instead of manual waits\n3. **Avoid `page.waitForTimeout()`** - Use proper waits like `waitForURL()` or `expect.poll()`\n4. **Use locator chaining** - `page.locator('.parent').locator('.child')`\n5. **Use `'.'` for baseURL navigation** - `page.goto('.')` navigates to the baseURL path; `page.goto('/')` goes to the root domain (common mistake)\n6. **Prefer relative paths** - For routes like `search/category/?query=test`, use paths without leading `/`\n7. **Avoid hardcoded test data** - The Algolia index data can change. Instead of checking exact product names, verify behavior (e.g., results changed, count > 0, URL updated)\n8. **Wait for results after navigation** - After pagination or filter changes, wait for new results to load before asserting\n9. **Don't use `scrollIntoViewIfNeeded()`** - Playwright auto-scrolls elements into view before clicking\n10. **Don't add redundant waits after URL waits** - If you wait for URL change, the UI has already updated; no need to also wait for element visibility\n\n## Flavor System\n\nThe main e2e suite tests multiple InstantSearch flavors:\n\n- `js` - instantsearch.js with vanilla JS\n- `js-umd` - instantsearch.js UMD build\n- `react` - React InstantSearch\n- `vue` - Vue InstantSearch\n\nTests run against example apps in `website/examples/{flavor}/e-commerce/`.\n\nTo run a single flavor:\n\n```bash\nE2E_FLAVOR=react yarn test:e2e\n```\n\n## IE11 Considerations\n\nIE11 tests are kept in WebdriverIO because Playwright doesn't support IE11. These tests:\n\n- Only run `js` and `js-umd` flavors\n- Require Sauce Labs for remote IE11 browser\n- Use the same test specs in `tests/e2e/specs/` and helpers in `tests/e2e/helpers/`\n\n## Debugging\n\n### Playwright\n\n```bash\n# Run with headed browser\nyarn workspace @instantsearch/e2e-tests test:playwright --headed\n\n# Run with UI mode (interactive debugging)\nyarn workspace @instantsearch/e2e-tests test:playwright --ui\n\n# Run specific test file\nyarn workspace @instantsearch/e2e-tests test:playwright specs/brand-and-query.spec.ts\n\n# Debug mode with inspector\nPWDEBUG=1 yarn workspace @instantsearch/e2e-tests test:playwright\n```\n\n### View Test Reports\n\nAfter running tests, open the HTML report:\n\n```bash\nnpx playwright show-report tests/e2e/playwright/playwright-report\n```\n\n## Troubleshooting\n\n### Tests timeout waiting for elements\n\n1. **Check if examples are built**: Run `yarn website:examples` before running tests\n2. **Check if the server starts**: The webServer uses port 3456 (not 5000, which conflicts with macOS AirPlay)\n3. **Check navigation**: Use `page.goto('.')` not `page.goto('/')` to navigate to baseURL\n\n### Port conflicts on macOS\n\nOn macOS, port 5000 is used by AirPlay Receiver (ControlCenter). The tests use port 3456 instead. If you need to change the port, update:\n\n- `tests/e2e/playwright/playwright.config.ts` (both `baseURL` and `webServer.command/url`)\n\n### SPA routing not working\n\nThe e-commerce examples use History API routing (e.g., `/search/Appliances/`). The `website/serve.json` file configures rewrites for these routes. If you add new SPA routes, update this file:\n\n```json\n{\n  \"rewrites\": [\n    {\n      \"source\": \"/examples/js/e-commerce/search/**\",\n      \"destination\": \"/examples/js/e-commerce/index.html\"\n    }\n  ]\n}\n```\n\n### Tests are flaky with multiple workers\n\nThe static server can handle 2 parallel workers. All configs use 2 workers for both CI and local runs.\n\nThe main suite retries once locally and twice in CI. The Next.js suites retry twice in CI and not at all locally.\n\n### Multiple elements matched\n\nIf you see \"strict mode violation\" errors about multiple elements:\n\n- SearchBox selectors are scoped to the header to avoid matching RefinementList search inputs\n- Use more specific selectors when targeting widgets that may appear multiple times\n\n### Different HTML structures across flavors\n\nThe JS and React examples have different HTML structures:\n\n- **JS examples**: Use `id=\"header\"` and `data-widget=\"searchbox\"` attributes\n- **React examples**: Use `className=\"header\"` (class instead of id) and render SearchBox directly inside header\n\nThe fixtures account for this with combined selectors like:\n\n```typescript\n'#header .ais-SearchBox [type=search], .header > .ais-SearchBox [type=search], [data-widget=\"searchbox\"] .ais-SearchBox [type=search]';\n```\n\nIf you add new selectors, ensure they work across all flavors.\n\n## Migration Notes: WebDriverIO to Playwright\n\nWhen migrating tests from WebDriverIO to Playwright, be aware of these differences:\n\n### URL Waiting\n\n- **WebDriverIO**: Used custom `waitForUrl()` that polls `browser.getUrl()` until match\n- **Playwright**: Use `await expect(page).toHaveURL(url)` which has built-in retrying\n\n### Element Selection\n\n- **WebDriverIO**: `$('.ais-Hits-item')` returns first match, clicking works on container elements\n- **Playwright**: `page.locator('.ais-Hits-item').first()` - clicking on container may not hit child link elements. Use `.locator('a')` to target links explicitly:\n\n  ```typescript\n  // WebDriverIO\n  const link = await $('.ais-Hits-item');\n  await link.click();\n\n  // Playwright - click the link inside the hit\n  const link = page.locator('.ais-Hits-item a').first();\n  await link.click();\n  ```\n\n### Browser Back Navigation\n\n- **WebDriverIO**: `browser.back()` worked reliably for SPA navigation\n- **Playwright**: `page.goBack()` works but may require `slowMo` in headless mode for Next.js App Router dynamic routes\n\n### Headless Mode Timing Issues\n\nPlaywright's headless mode runs faster than headed mode, which can cause timing issues with SPA navigation and state updates. For Next.js App Router tests involving browser back/forward navigation with dynamic routes, add `slowMo` at the test file level (not globally, to avoid slowing down all tests):\n\n```typescript\nimport { test, expect } from '@playwright/test';\n\n// slowMo is required for browser back/forward navigation tests in headless mode\ntest.use({ launchOptions: { slowMo: 500 } });\n\ntest.describe('browser back/forward buttons', () => {\n  // tests that use page.goBack() or page.goForward()\n});\n```\n\nThis gives the browser enough time to process JavaScript events like popstate handlers used by InstantSearch routing, while keeping other tests fast.\n\n<!-- File: .claude/skills/port-widget/agents/openai.yaml (Tokens: ~67 | Category: anthropic-skill) -->\ninterface:\n  display_name: 'Port InstantSearch Widget'\n  short_description: 'Port widgets across InstantSearch flavors'\n  default_prompt: 'Use $port-widget to port a widget or connector-driven feature across the InstantSearch JavaScript, React, and Vue packages in this repo.'\n\npolicy:\n  allow_implicit_invocation: true\n\n<!-- File: .claude/skills/port-widget/references/js-flavor.md (Tokens: ~345 | Category: anthropic-skill) -->\n# JavaScript Flavor (`instantsearch.js`)\n\n## Reach for these precedents first\n\n- Shared UI via `instantsearch-ui-components`: `hits`, `related-products`, `trending-items`, `filter-suggestions`\n- Older Preact widget with templates and CSS helpers: `refinement-list`, `menu`, `pagination`\n\n## File layout\n\n```text\npackages/instantsearch.js/src/widgets/<widget>/\n  <widget>.tsx\n  defaultTemplates.tsx        # only when the widget exposes templates\n  __tests__/<widget>-test.tsx # optional flavor-specific tests\n```\n\n## Wrapper pattern\n\n- Import the connector from `../../connectors/<widget>/connect<Pascal>`.\n- Build a `createRenderer(...)` helper that closes over `containerNode`, classes, templates, and any normalized props.\n- Use `getContainerNode(container)` and throw via `createDocumentationMessageGenerator` when `container` is missing.\n- Mount with `render(<UiComponent ... />, containerNode)` and unmount with `render(null, containerNode)`.\n- Return `$$widgetType: 'ais.<camelName>'`.\n\n## UI choice\n\n- Prefer `instantsearch-ui-components` when the React and JS widgets should share markup.\n- Keep local JSX only when the JS widget still depends on legacy template plumbing or DOM structure that is not shared elsewhere.\n- If templates are supported, follow `TemplateComponent` and `prepareTemplateProps` patterns from a close sibling widget rather than inventing a new API.\n\n## Registration checklist\n\n- Export the connector from `packages/instantsearch.js/src/connectors/index.ts`.\n- Export the widget from `packages/instantsearch.js/src/widgets/index.ts`.\n\n<!-- File: .claude/skills/port-widget/references/react-flavor.md (Tokens: ~696 | Category: anthropic-skill) -->\n# React Flavor (`react-instantsearch`)\n\n## Package split\n\n- `packages/react-instantsearch-core`: hooks that wrap `instantsearch.js` connectors\n- `packages/react-instantsearch`: public widgets and optional React-only UI components\n\n## Hook pattern\n\nFile: `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts`\n\n- Import the connector from `instantsearch.js/es/connectors/<widget>/connect<Pascal>`.\n- Import `useConnector` and `AdditionalWidgetProperties` from `../hooks/useConnector`.\n- Export `Use<Pascal>Props = <Pascal>ConnectorParams`.\n- Pass `additionalWidgetProperties` through to `useConnector(...)`.\n\n## Widget pattern\n\nChoose the closest precedent before writing code:\n\n- Shared UI component wrapper: `Hits.tsx`, `RelatedProducts.tsx`, `TrendingItems.tsx`, `FilterSuggestions.tsx`\n- React-only presentational UI in `src/ui`: `SearchBox.tsx`, `RangeInput.tsx`, `RefinementList.tsx`, `Menu.tsx`, `MenuSelect.tsx`, `SortBy.tsx`\n\n### If the UI is shared via `instantsearch-ui-components`\n\n- Instantiate the factory with `createElement as Pragma` and `Fragment`.\n- Keep connector params separate from UI-only props.\n- Pass `{ $$widgetType: 'ais.<camelName>' }` as the second argument to the hook.\n- Use `useInstantSearch()` only when the UI needs search status or other top-level state.\n- Only add `useMemo` or `useCallback` when an adjacent widget uses them to stabilize component props passed into a shared UI factory.\n\n### If no shared factory exists (React-only UI)\n\n- Create `packages/react-instantsearch/src/ui/<Pascal>.tsx` for the presentational component. This applies to all widgets without a shared factory — even simple ones like `MenuSelect` (a plain `<select>`).\n- Keep the widget file focused on mapping hook state and event handlers into the UI component.\n\n### Variant widgets (shared connector)\n\nSome widgets reuse another widget's hook instead of having a dedicated one. For example, `MenuSelect` uses `useMenu` (not `useMenuSelect`). In this case:\n\n- Import the existing hook directly in the widget file.\n- Pick only the relevant connector params (e.g., exclude `showMore`/`showMoreLimit` for `MenuSelect`).\n- Set a distinct `$$widgetType` (e.g., `'ais.menuSelect'`).\n\n## Registration checklist\n\n- Export the hook from `packages/react-instantsearch-core/src/index.ts` (skip for variant widgets that reuse an existing hook).\n- Export the widget from `packages/react-instantsearch/src/widgets/index.ts`.\n- If the widget is public, `packages/react-instantsearch/src/index.ts` already re-exports it through `./widgets`.\n- Add the widget to the switch in `packages/react-instantsearch/src/widgets/__tests__/__utils__/all-widgets.tsx` with required minimum props (e.g., `attribute=\"brand\"`).\n- In `packages/react-instantsearch/src/__tests__/common-widgets.test.tsx`:\n  - Replace the `throw new Error('X is not supported in React InstantSearch')` with real setup code.\n  - Remove the `skippedTests` entry for the widget in `testOptions`.\n\n<!-- File: .claude/skills/port-widget/references/testing.md (Tokens: ~1233 | Category: anthropic-skill) -->\n# Testing and Examples\n\n## Start with the common suites\n\n### Wrapper or public widget change\n\n- Update `tests/common/widgets/<widget>/`.\n- Register or unskip the suite in each flavor's `common-widgets.test.*`:\n  - `packages/instantsearch.js/src/__tests__/common-widgets.test.tsx`\n  - `packages/react-instantsearch/src/__tests__/common-widgets.test.tsx`\n  - `packages/vue-instantsearch/src/__tests__/common-widgets.test.js`\n- For previously unsupported widgets: replace the `throw new Error('X is not supported in ...')` placeholder with real setup code, and remove the `skippedTests` entry in `testOptions`.\n\n### Connector or hook API change\n\n- Update `tests/common/connectors/<widget>/`.\n- Register or unskip the suite in:\n  - `packages/instantsearch.js/src/__tests__/common-connectors.test.tsx`\n  - `packages/react-instantsearch/src/__tests__/common-connectors.test.tsx`\n  - `packages/vue-instantsearch/src/__tests__/common-connectors.test.js`\n\n## Flavor-specific tests\n\n- JavaScript: `packages/instantsearch.js/src/widgets/<widget>/__tests__/`\n- React: `packages/react-instantsearch/src/widgets/__tests__/`\n- Vue: colocated component or package tests under `packages/vue-instantsearch/src/__tests__/`\n\nAdd flavor-specific tests only for behavior that the shared suites do not cover, such as:\n\n- slot rendering differences in Vue\n- React-only UI plumbing\n- template or CSS-class edge cases in `instantsearch.js`\n- registry coverage such as React's `all-widgets` helpers\n\n## Shared cross-widget suites\n\n- Search `tests/common/shared/` and `tests/common/shared-composition/` when the port affects routing, searchable behavior, or cross-widget composition.\n- Only touch those suites when the new widget changes shared behavior rather than a single widget contract.\n\n## Examples\n\n- Search existing examples before editing; not every user-facing widget belongs in the e-commerce demos.\n- E-commerce apps live in:\n  - `examples/js/e-commerce/src/`\n  - `examples/react/e-commerce/`\n  - `examples/vue/e-commerce/src/`\n- Recommendation, chat, and query-suggestion features already appear in getting-started or query-suggestions examples for JS and React.\n\n## Vue-specific test timing\n\n### State delivery is async for some connectors\n\nMost connectors (e.g. `connectRefinementList`) deliver state synchronously during `mountApp` because the mock search client responds immediately. Newer connectors like `connectChat` deliver state asynchronously. The Vue test setup must account for this:\n\n```javascript\nmountApp({ render: ... }, container);\nawait nextTick();                                      // Vue tick\nawait new Promise((resolve) => setTimeout(resolve, 0)); // macrotask for async state\nawait nextTick();                                      // Vue tick to re-render\n```\n\n### Provide a custom `act` for Vue\n\nVue batches DOM updates asynchronously, unlike React which re-renders synchronously within event handlers. Tests that `await act(async () => { await wait(0); })` work because `act` wraps the wait. Provide a custom `act` in `testOptions` that flushes Vue's update queue:\n\n```javascript\ncreateXxxWidgetTests: {\n  act: async (cb) => {\n    await cb();\n    await nextTick();\n  },\n},\n```\n\nTests that click then immediately assert DOM changes (without `act`) will still fail in Vue. These are known limitations of the shared test suite's React-centric timing assumptions. Document them in the `testOptions` but avoid skipping the entire test suite.\n\n### Flavored test params\n\nFor `flavored` test suites (like Chat), `runTestSuites` extracts the flavor-specific params via `widgetParams[flavor]`. The Vue setup function receives only the Vue portion. Ensure the Vue params include all required connector params (e.g. `agentId`, `chat`) — empty `{}` will cause connector errors.\n\nWhen Vue params need component functions (tools with `layoutComponent`, custom `headerComponent`), use `import { h as vueH } from 'vue'` in the `.tsx` test file and return VNodes: `() => vueH('div', { class: 'foo' }, 'text')`.\n\n### skippedTests granularity\n\n`skippableDescribe` only skips entire describe blocks by name. Individual test names in `skippedTests` have no effect unless the test file uses `skippableTest`. The shared chat/options/templates/translations test files use plain `test()`, so individual test skipping is not available for Chat widget tests.\n\n## Useful commands\n\n```bash\n# Common widget/connector tests\nnpx jest packages/instantsearch.js/src/__tests__/common-widgets.test.tsx --no-coverage\nnpx jest packages/instantsearch.js/src/__tests__/common-connectors.test.tsx --no-coverage\nnpx jest packages/react-instantsearch/src/__tests__/common-widgets.test.tsx --no-coverage\nnpx jest packages/react-instantsearch/src/__tests__/common-connectors.test.tsx --no-coverage\nnpx jest packages/vue-instantsearch/src/__tests__/common-widgets.test.js --no-coverage\n\n# Run only tests matching a specific widget name\nnpx jest packages/react-instantsearch/src/__tests__/common-widgets.test.tsx --testNamePattern=\"MenuSelect\" --no-coverage\n\n# Flavor-specific tests\nnpx jest packages/react-instantsearch/src/widgets/__tests__ --no-coverage\nnpx jest packages/instantsearch.js/src/widgets/<widget> --no-coverage\n\n# Update snapshots after adding new exports\nnpx jest packages/react-instantsearch/src/widgets/__tests__ --no-coverage -u\n\n# E2E\nyarn website:examples\nE2E_FLAVOR=react E2E_BROWSER=chromium yarn test:e2e\n```\n\n<!-- File: .claude/skills/port-widget/references/vue-flavor.md (Tokens: ~597 | Category: anthropic-skill) -->\n# Vue Flavor (`vue-instantsearch`)\n\n## Current patterns in this repo\n\n- Slot-heavy SFCs: `RefinementList.vue`, `Menu.vue`, `Pagination.vue`\n- Render-function wrappers around shared UI components: `Hits.js`, `Highlighter.js`\n- Framework glue helpers: `mixins/widget.js`, `mixins/suit.js`, `util/vue-compat.js`\n\n## Choose the wrapper shape deliberately\n\n### Use a `.vue` SFC when\n\n- the widget exposes multiple named slots\n- the DOM structure is easiest to express in template syntax\n- the closest existing widget is already an SFC\n\n### Use a `.js` render function when\n\n- the widget reuses `instantsearch-ui-components`\n- the wrapper mostly adapts connector state into a shared UI factory\n- the widget needs `renderCompat(...)` or lower-level vnode control\n\n## Implementation rules\n\n- Import connectors from `instantsearch.js/es/connectors/index`.\n- Use `createWidgetMixin({ connector: ... }, { $$widgetType: 'ais.<camelName>' })`.\n- Use `createSuitMixin({ name: '<Pascal>' })` for BEM classes. When the widget delegates all rendering to a shared `createXxxComponent` factory, the suit mixin's `suit()` method goes unused but the `classNames` prop it provides is still convenient. Pass `this.classNames` directly to the shared component's `classNames` prop (semantic keys like `{ root, container }`, not BEM keys).\n- Expose connector params through a computed `widgetParams()` object.\n- When reusing shared UI factories, wrap the render function with `renderCompat(...)` and map `this.classNames` into the `classNames` prop expected by the shared component.\n- Prefer `getScopedSlot` or `getDefaultSlot` helpers over direct slot access when matching render-function components.\n- In render-function callbacks that reference connector state (e.g. `onSubmit`, `onInput`), read from `this.state.xxx` instead of destructured locals. Vue batches re-renders, so destructured values become stale between synchronous user interactions (type then click).\n\n## Registration checklist\n\n- Export the component from `packages/vue-instantsearch/src/widgets.js`.\n- Do not manually edit `src/plugin.js` or `src/instantsearch.js` for normal widget additions; they already derive registration and exports from `widgets.js`.\n- Search `packages/vue-instantsearch/src/__tests__/common-widgets.test.js` and `common-connectors.test.js` for placeholder \"not supported\" branches before deciding the widget is genuinely absent.\n\n## Missing-feature caution\n\n- Recommendation widgets, chat, and filter suggestions already have connector-level test placeholders in Vue, but several still throw explicit \"not supported\" errors at the widget layer.\n- If you port one of those widgets, remove the placeholder failure and replace it with real setup code plus the component implementation.\n\n<!-- File: .claude/skills/port-widget/SKILL.md (Tokens: ~1509 | Category: anthropic-skill) -->\n---\nname: port-widget\ndescription: Port or introduce an InstantSearch widget or connector-driven feature across this monorepo's JavaScript, React, and Vue flavors. Use when the task is to add a missing wrapper around an existing connector, bring a widget from one flavor to another, or implement a new shared widget end-to-end in `instantsearch.js`, `react-instantsearch`, and `vue-instantsearch`. Triggers include requests like \"port X to vue\", \"add Y to react-instantsearch\", \"make Z available in all flavors\", or \"implement the missing InstantSearch wrapper for this connector\".\n---\n\n# Port InstantSearch Widgets Across Flavors\n\n## Start with the repo audit\n\n- Run `python3 scripts/audit_widget_coverage.py <widget-kebab-name>` from this skill folder before editing.\n- Use `--repo /path/to/instantsearch` if your current working directory is not inside the InstantSearch repo.\n- Treat placeholder Vue failures in `packages/vue-instantsearch/src/__tests__/common-widgets.test.js` or `common-connectors.test.js` as evidence that the connector exists but the Vue wrapper still needs work.\n\n## Layer map\n\n- Connector: `packages/instantsearch.js/src/connectors/<widget>/connect<Pascal>.ts`\n- JS widget: `packages/instantsearch.js/src/widgets/<widget>/<widget>.tsx`\n- React hook: `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts`\n- React widget: `packages/react-instantsearch/src/widgets/<Pascal>.tsx`\n- Optional React UI: `packages/react-instantsearch/src/ui/<Pascal>.tsx`\n- Vue wrapper: `packages/vue-instantsearch/src/components/<Pascal>.vue` or `.js`\n- Shared widget tests: `tests/common/widgets/<widget>/`\n- Shared connector tests: `tests/common/connectors/<widget>/`\n\n## Variant widgets\n\nSome widgets reuse another widget's connector with different defaults or UI. For example, `menuSelect` uses `connectMenu`/`useMenu` (not a dedicated `connectMenuSelect`). The audit will show `no` for connector and hook rows — this is expected. The `$$widgetType` still differs (`ais.menuSelect` vs `ais.menu`). When porting a variant widget, skip connector/hook creation and reuse the existing hook directly in the widget file.\n\nKnown variants: `menuSelect` → `connectMenu`/`useMenu`.\n\n## Workflow\n\n1. Decide the scope.\n   - Existing connector, missing wrapper: keep the connector API unchanged and port only the wrapper plus wrapper tests.\n   - Variant widget (shared connector, different UI): skip connector/hook creation; reuse the existing hook and set a distinct `$$widgetType`.\n   - Missing connector or changed render state: start in `instantsearch.js`, then update every flavor and both common test suites.\n   - Vue port for a newer recommendation, chat, or filter-suggestions feature: inspect `Hits.js`, `Highlighter.js`, `DynamicWidgets.js`, and `util/vue-compat.js` before designing the wrapper.\n2. Match a real precedent.\n   - Pick one close widget in the target flavor and one close widget in another flavor.\n   - Reuse the same prop names, slot or component escape hatches, `$$widgetType`, and test style.\n3. Build from the bottom up.\n   - Connector exports belong in `packages/instantsearch.js/src/connectors/index.ts`.\n   - JS widget exports belong in `packages/instantsearch.js/src/widgets/index.ts`.\n   - React hook exports belong in `packages/react-instantsearch-core/src/index.ts`.\n   - React widget exports belong in `packages/react-instantsearch/src/widgets/index.ts`; `packages/react-instantsearch/src/index.ts` already re-exports widgets.\n   - Vue exports belong in `packages/vue-instantsearch/src/widgets.js`; `src/instantsearch.js` and the plugin re-export and register from there automatically.\n4. Choose the right sharing model.\n   - JS and React: prefer `instantsearch-ui-components` when the markup can be shared.\n   - React: create `src/ui/<Pascal>.tsx` whenever the widget has no shared factory in `instantsearch-ui-components`. This includes simple widgets like `MenuSelect` (a plain `<select>`) — `src/ui/` is for all React-rendered markup, not only complex cases.\n   - Vue: use `.vue` SFCs for slot-heavy markup and `.js` render functions with `renderCompat` when reusing `instantsearch-ui-components`.\n5. Wire tests before finishing.\n   - Update `tests/common/widgets/<widget>/` whenever the wrapper behavior changes.\n   - Update `tests/common/connectors/<widget>/` whenever connector params or render state change.\n   - Register the suite in each flavor's `common-widgets.test.*` and `common-connectors.test.*`.\n   - Replace any `throw new Error('X is not supported in ...')` placeholder with real setup code in the target flavor's `common-widgets.test.*`.\n   - Remove the corresponding `skippedTests` entry in `testOptions` for that widget.\n   - For React: always add the widget to the switch in `packages/react-instantsearch/src/widgets/__tests__/__utils__/all-widgets.tsx` with the required minimum props.\n6. Check examples only when the widget is user-facing.\n   - Search existing examples first. Recommendation, chat, and query-suggestion widgets already live in getting-started or query-suggestions examples, not only the e-commerce apps.\n   - Add to `examples/*/e-commerce` only when the widget fits the shared storefront UX or existing Playwright coverage.\n\n## Reminders\n\n- Keep `$$widgetType` aligned across flavors.\n- Do not invent new Vue patterns; match `createWidgetMixin`, `createSuitMixin`, scoped slots, and `renderCompat`.\n- Do not add memoization hooks in React unless an adjacent widget uses them for the same reason.\n\n### Cross-package API migration checks\n\n- Update all affected layers in one pass when moving or splitting behavior: wrapper APIs, shared UI components, layout/type contracts, exports, and tests. Wrapper-only edits create cross-package type drift.\n- If you remove a required prop or render path, either delete it end-to-end or make downstream contracts optional in the same change; partial migrations often fail during declaration builds.\n- Before finishing, grep for legacy names (old props, options, class names, template keys, translations keys) to catch leftovers across flavors.\n- Keep control contracts explicit and consistent when behavior is externally controlled (e.g., imperative handles or widget methods), so external integrations do not depend on internal layout details.\n- Do not add ad-hoc top-level markdown change logs unless requested; keep source of truth in code, tests, and existing docs.\n- Always run monorepo build validation (`yarn build --ignore='example*'`) after cross-package API changes; it catches declaration and export drift that local tests may miss.\n\n## References\n\n- JS wrappers: [references/js-flavor.md](references/js-flavor.md)\n- React wrappers: [references/react-flavor.md](references/react-flavor.md)\n- Vue wrappers: [references/vue-flavor.md](references/vue-flavor.md)\n- Testing and examples: [references/testing.md](references/testing.md)\n\n<!-- File: packages/algoliasearch-helper/CLAUDE.md (Tokens: ~695 | Category: anthropic-skill) -->\n# algoliasearch-helper\n\nThe **low-level search-state manager** that sits _underneath_ InstantSearch connectors. It's a separately-published, long-stable npm package (`algoliasearch-helper`, own semver — currently 3.x) that wraps the `algoliasearch` JS client with a higher-level, stateful API: it tracks search parameters, builds the actual Algolia queries, parses responses, and emits events. Every connector in `instantsearch.js` reads and writes this helper's state — but the connector is the layer you change, not this one.\n\n> **⚠️ Rarely the right place for a change/fix/update.** This package is mature and broadly depended on — InstantSearch (all flavors) plus other Algolia front-end libraries build on it. Almost all widget/connector behavior is implemented _on top of_ the helper, not in it. Touch it **only** for a genuine state-manager or response-parsing bug, or to add a real new search-parameter primitive. Any change here is a **public-API, independently-versioned** change with a blast radius far beyond this repo — flag it explicitly and prefer fixing the connector instead.\n\n## What's in it\n\nEntry: `algoliasearchHelper(client, index, opts)` → an `AlgoliaSearchHelper` (an `EventEmitter`). Core pieces under `src/`:\n\n- **`SearchParameters/`** — the **immutable** query state (query, `facets`/`disjunctiveFacets`/`hierarchicalFacets`, numeric & tag refinements, `page`, etc.). Setters return a _new_ instance; never mutate in place. This is the object connectors mutate via `getWidgetSearchParameters`.\n- **`SearchResults/`** — parses the raw Algolia response into a richer structure: facet values, `facets_stats`, and hierarchical facet trees (`generate-hierarchical-tree.js`).\n- **`requestBuilder.js`** — turns `SearchParameters` into the actual query payload(s); notably **splits disjunctive faceting into multiple queries**.\n- **`DerivedHelper/`** — sub-requests derived from a main helper (federated / multi-index search); this is what powers the `index` widget.\n- **`RecommendParameters/` + `RecommendResults/`** — the equivalent state/results pair for the Recommend API.\n- **`functions/`** — small internal lodash-style utilities (`merge`, `omit`, `orderBy`, …).\n\nEvents emitted by the helper (what InstantSearch's lifecycle listens to): `change`, `search`, `result`, `error`, `fetch`, `searchOnce`, `searchForFacetValues`, `searchQueueEmpty`, `recommendQueueEmpty`.\n\n## Conventions that differ from the rest of the monorepo\n\n- **Plain CommonJS JavaScript**, not the TS-strict source the other packages use. Public types are **hand-written** in `index.d.ts` — update them by hand if you change the surface.\n- Has its own **`README.md` + `CHANGELOG.md`** (don't fold into the monorepo's). Build is Rollup (`yarn workspace algoliasearch-helper build`); tests run via its own `scripts/test.sh` (`yarn workspace algoliasearch-helper test`).\n\n<!-- File: packages/create-instantsearch-app/docs/custom-templates.md (Tokens: ~1036 | Category: anthropic-skill) -->\n# Creating a custom template\n\nBesides all the InstantSearch templates provided, the Create InstantSearch API enables you to create your own template.\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Defining the template](#defining-the-template)\n  - [`libraryName`](#libraryname)\n  - [`tasks`](#tasks)\n- [Injecting values to the template](#injecting-values-to-the-template)\n- [Using the template](#using-the-template)\n- [Examples](#examples)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## Defining the template\n\nOnce you've created a project directory, you can turn it into a template by adding a file `.template.js` at its root. This is the configuration file that drives `createInstantSearchApp()` to bootstrap your application.\n\nThis definition file returns a object with several properties.\n\n<h6 align=\"center\">.template.js</h6>\n\n```javascript\nconst { execSync } = require('child_process');\n\nmodule.exports = {\n  libraryName: 'react-instantsearch',\n  tasks: {\n    install(config) {\n      execSync(`cd ${config.path} && npm install`);\n    },\n    teardown(config) {\n      console.log('Begin by running: npm start');\n    },\n  },\n};\n```\n\n### `libraryName`\n\n> `string` (optional) | no default\n\nThe name of the [npm](https://www.npmjs.com) library to fetch versions for.\n\n### `tasks`\n\n> `object` | defaults as follows\n\nThe tasks are hooks that define your [application generation lifecycle](#lifecycle). Each of these hooks are functions which receives the [app configuration](#configuration-object) as parameters. You can use this configuration to drive the application generation.\n\n#### `setup(config: object)`\n\n> No default\n\nCan be leveraged to check the project requirements.\n\n#### `build(config: object)`\n\n> Defaults to copying and injecting the values\n\nCan be leveraged to generate the project differently.\n\n#### `install(config: object)`\n\n> No default\n\nCan be leveraged to install dependencies with a package manager.\n\n#### `clean(config: object)`\n\n> Defaults to deleting the project which installation has failed\n\nCan be leveraged to warn the user that the installation has failed.\n\n#### `teardown(config: object)`\n\n> No default\n\nCan be leveraged to display instructions to run the app.\n\n<h6 align=\"center\" id=\"lifecycle\">Application generation lifeycle</h6>\n\n![Setup → Build → Install → Clean (if installation fails) → Teardown](https://user-images.githubusercontent.com/6137112/41421858-f838c2a6-6ff7-11e8-8cef-4cc07f1f4f44.png)\n\n<h6 align=\"center\" id=\"configuration-object\"><code>config</code> object</h6>\n\n```js\n{\n  name: 'app-name',\n  path: '/dev/app-name',\n  template: 'InstantSearch.js',\n  libraryVersion: '2.9.0',\n  appId: 'APP_ID',\n  apiKey: 'API_KEY',\n  indexName: 'INDEX_NAME',\n  attributesToDisplay: ['title', 'description'],\n  installation: true,\n  silent: false,\n}\n```\n\n## Injecting values to the template\n\nBy default, the `build` task supports the [Handlebars](https://handlebarsjs.com) syntax to inject values passed to Create InstantSearch App. You can explicitly suffix the template files with `.hbs` or leave them as is (`.js` in this case).\n\n<h6 align=\"center\">App.js.hbs</h6>\n\n```handlebars\nimport React, { Component } from 'react';\nimport algoliasearch from 'algoliasearch/lite';\nimport {\n  InstantSearch,\n  Hits,\n  SearchBox,\n  {{#if attributesToDisplay}}\n  Highlight,\n  {{/if}}\n} from 'react-instantsearch-dom';\n\nconst searchClient = algoliasearch(\n  '{{appId}}',\n  '{{apiKey}}'\n);\n\n<InstantSearch searchClient={searchClient} indexName=\"{{indexName}}\">\n  <SearchBox placeholder=\"{{searchPlaceholder}}\" />\n  {/* ... */}\n</InstantSearch>\n```\n\n## Using the template\n\nCreate InstantSearch App reads your `.template.js` file and gets the specified tasks in the provided template.\n\nUsing the API:\n\n```javascript\nconst app = createInstantSearchApp('my-app', {\n  template: '/path/to/my-custom-template',\n});\n\napp.create();\n```\n\nUsing the CLI:\n\n```\ncreate-instantsearch-app my-app --template /path/to/my-custom-template\n```\n\n## Examples\n\nYou can find all the officially supported templates in the [`src/templates/`](../src/templates) folder.\n\n<!-- File: packages/create-instantsearch-app/docs/deploy.md (Tokens: ~241 | Category: anthropic-skill) -->\n# Deploy an InstantSearch app\n\nCreate InstantSearch App generates applications that are ready to be deployed. They rely on bundlers and framework toolkits (Parcel, Create React App, Vue CLI, etc.).\n\nYour app can be deployed in a single command, assuming you're located in its folder.\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [CodeSandbox](#codesandbox)\n- [Surge](#surge)\n- [Vercel](#vercel)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## CodeSandbox\n\n```\nnpx codesandbox .\n```\n\n[Learn more about CodeSandbox →](https://codesandbox.io)\n\n## Surge\n\n```\nnpx surge\n```\n\n[Learn more about Surge →](https://surge.sh)\n\n## Vercel\n\n```\nnpx vercel\n```\n\n[Learn more about Vercel →](https://vercel.com)\n\n<!-- File: packages/instantsearch-ui-components/CLAUDE.md (Tokens: ~880 | Category: anthropic-skill) -->\n# instantsearch-ui-components\n\nThe **framework-agnostic UI layer** — the shared markup/layout for widgets, reused by every flavor. This is where a widget's DOM structure and `ais-*` CSS classes live for **newer** widgets. (Older widgets still define their layout per-flavor; see \"Old vs new\" below.)\n\n## The factory + renderer pattern\n\nComponents are **not** React/Preact/Vue components directly. Each is a factory that takes a renderer and returns a component:\n\n```tsx\n/** @jsx createElement */\nimport { cx } from '../lib';\nimport type { Renderer, ComponentProps } from '../types';\n\nexport function createHitsComponent({ createElement, Fragment }: Renderer) {\n  return function Hits(props) {\n    return <div className={cx('ais-Hits', props.classNames.root)}>…</div>;\n  };\n}\n```\n\n- **`Renderer` = `{ createElement, Fragment }`** is _injected_ by the consumer (`src/types/Renderer.ts`). Defaults are Preact's, but React passes `react`'s `createElement` and Vue passes its own `h` (wired up via the `renderCompat` helper in `vue-instantsearch`). **Never import `preact`/`react`/`vue` directly** — go through the injected renderer. The `/** @jsx createElement */` pragma at the top of each file is what wires JSX to the injected function.\n- Class names go through **`cx`** (`src/lib/cx.ts`) and follow the `ais-<Widget>-<element>` contract that `instantsearch.css` themes — treat those class names as a public API.\n- Props are typed with `ComponentProps<'div'>`-style helpers from `src/types`; expose a `classNames` partial so consumers can extend styling.\n\n## Layout\n\n- `src/components/<Name>.tsx` — a `create<Name>Component` factory. Exported from `src/components/index.ts` → `src/index.ts`.\n- Grouped families: `src/components/autocomplete/`, `src/components/chat/`, `src/components/recommend-shared/` (+ the recommend widgets `RelatedProducts`, `FrequentlyBoughtTogether`, `LookingSimilar`, `TrendingItems`, `TrendingFacets`).\n- `src/lib/` — `cx`, `stickToBottom`, shared `utils`. `src/types/` — `Renderer`, `ComponentProps`, `Hooks`, `Recommend`, `shared`.\n- Runtime deps are minimal on purpose (`@swc/helpers`); **no framework as a dependency**. Markdown rendering uses a vendored copy of `markdown-to-jsx` in `src/lib/markdown-to-jsx/` (React removed, see its README); it is linted, formatted and type-checked like the rest.\n\n## Old vs new (important)\n\n- **New widgets:** layout lives here and is consumed by all flavors — React via `import { create<Name>Component } from 'instantsearch-ui-components'`, Vue via the `renderCompat` helper, JS via the Preact renderer.\n- **Older widgets:** layout was defined per-flavor — JS in `packages/instantsearch.js/src/components/` (Preact), React in `packages/react-instantsearch/src/ui/`. The migration direction is to consolidate that markup here; some JS-package components are now thin wrappers that import from this package.\n- When adding/changing **shared** layout, do it here. When touching a widget that still defines its own markup, check whether it should be migrated rather than forked.\n\n## Working here\n\n```bash\nyarn workspace instantsearch-ui-components build       # tsc/rollup + build:types\nyarn jest packages/instantsearch-ui-components/src/components/__tests__/<Name>   # from repo root\n```\n\n- Tests are co-located in `src/components/__tests__/`. Test the component by passing a concrete renderer (e.g. Preact's) — assert rendered structure and class names, not implementation.\n- A markup or class-name change here **ripples to React, Vue, and JS at once** — verify consumers and keep the `ais-*` contract stable (a class rename is a breaking change that also touches `instantsearch.css`).\n\n<!-- File: packages/instantsearch.js/CLAUDE.md (Tokens: ~592 | Category: anthropic-skill) -->\n# instantsearch.js (core / vanilla JS)\n\nThe core package. **Every connector lives here and is consumed by all flavors** — React hooks and Vue components wrap these. A behavior change usually belongs in a connector here, not in a flavor package.\n\n## Layout\n\n- `src/connectors/<name>/connect<Pascal>.ts` — framework-agnostic logic. Signature: `connect<Widget>(renderFn, unmountFn)` returns a widget factory. Owns state, lifecycle, and search-param mutations. Exported from `src/connectors/index.ts`.\n- `src/widgets/<name>/<name>.tsx` — vanilla JS widget = connector + default template/rendering. Exported from `src/widgets/index.ts`.\n- `src/components/` — the **JS flavor's Preact components** (the widgets' markup). This is the _legacy_ home for widget layout; newer widgets pull their layout from the shared `instantsearch-ui-components` package instead, and some components here are now thin wrappers that import from it. Add **new shared** markup to `instantsearch-ui-components`, not here.\n- `src/lib/` — the InstantSearch runtime: lifecycle orchestration, routing, helper integration.\n- `src/lib/ai-lite/` — hand-written reimplementation of the Vercel AI SDK's UI layer (`UIMessage` parts, the SSE chunk protocol, the `Chat` state machine `connectChat` drives). It replaced `ai@^5.0.18` in #6880 and tracks nothing upstream: AI SDK fixes land on the `ai@6` line and never reach us. **Before changing its semantics, diff the matching file under [`vercel/ai/packages/ai/src/ui`](https://github.com/vercel/ai/tree/main/packages/ai/src/ui)** — the auto-continue predicate silently missed vercel/ai#9944 (don't resend provider-executed tool turns) for months. Deliberate gaps: no tool-approval flow, no metadata/data-part schema validation, no `finishReason`.\n- `src/types/` — public type definitions.\n\n## Working here\n\n```bash\nyarn workspace instantsearch.js build      # rollup → esm + cjs + umd + .d.ts\nyarn jest packages/instantsearch.js/src/connectors/<name>   # run one connector's tests (from repo root)\n```\n\n- Connectors return a typed render state — don't return untyped state (oxlint rule).\n- A new widget that only varies presentation should **reuse an existing connector** (e.g. `menuSelect` reuses `connectMenu`) and set a distinct `$$widgetType`; don't fork the connector.\n- Cross-flavor behavior is covered by `tests/common/connectors/<name>/`; flavor-agnostic logic tests go there, JS-specific ones stay in `__tests__/`.\n\n<!-- File: packages/react-instantsearch-nextjs/CLAUDE.md (Tokens: ~529 | Category: anthropic-skill) -->\n# react-instantsearch-nextjs (Next.js App Router)\n\nSSR integration for the **App Router** (RSC + streaming). Peer-deps `next` + `react-instantsearch` — it **composes** react-instantsearch, never reimplements search. This is the non-trivial Next.js package; the subtle part is server-render + hydration.\n\n## Public surface\n\n- `InstantSearchNext` (`src/InstantSearchNext.tsx`) — the entry component, a drop-in for `<InstantSearch>` inside the App Router. It does SSR **for you**, so you don't hand-wire the plain-react-instantsearch `getServerState` + `InstantSearchSSRProvider` dance. Props: `routing` (boolean | object) and an optional `instance`.\n\n## How the SSR machinery fits together\n\n- `InitializePromise.ts` + `TriggerSearch.ts` — run the search **during the server (RSC) render** so results are available before HTML is sent.\n- `createInsertHTML.tsx` + `htmlEscape.ts` — serialize that initial server state and **inject it into the streamed HTML** for hydration. `htmlEscape` is a **safety boundary** (escapes the injected payload so it can't break out of the script/HTML context) — don't bypass it when touching the insert path.\n- `useInstantSearchRouting.ts`, `useNextHeaders.tsx`, `useDynamicRouteWarning.ts` — routing via `next/navigation` (not `next/router`). Dynamic routes are warned about (`useDynamicRouteWarning`) because they interact poorly with InstantSearch URL sync.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch-nextjs build\nyarn jest packages/react-instantsearch-nextjs/src/__tests__/<name>   # unit tests, from repo root\nyarn workspace react-instantsearch-nextjs test:e2e                   # Playwright (App Router)\n```\n\n- **Hydration is the failure mode.** A change to the initialize/trigger/insert path can produce server/client markup mismatch — verify there's no hydration warning and that the first paint already reflects server results.\n- e2e lives in `__tests__/e2e/` (Playwright). Browser back/forward on dynamic App Router routes needs file-level `slowMo` in headless mode — see `.claude/rules/e2e.md`.\n- Treat SSR/RSC/hydration work here as **reasoning-heavy** — it's the one corner of the React surface that isn't bounded widget wiring.\n\n<!-- File: packages/react-instantsearch-router-nextjs/CLAUDE.md (Tokens: ~444 | Category: anthropic-skill) -->\n# react-instantsearch-router-nextjs (Next.js Pages Router)\n\nA **thin routing adapter** for the **Pages Router** — not a full SSR layer. It wraps InstantSearch's history router so URL sync plays well with `next/router`. The actual SSR is the standard react-instantsearch `getServerState` + `InstantSearchSSRProvider` pattern in your app, **not** here.\n\n## Public surface\n\n- `createInstantSearchRouterNext({ singletonRouter, serverUrl?, ... })` (`src/index.ts`) — returns a `Router` you pass to `<InstantSearch routing={{ router }}>`. Tagged `$$type: 'ais.nextjs'`.\n\n## What it actually does (and why)\n\n- Builds on `instantsearch.js/es/lib/routers/history`; on the server (`typeof window === 'undefined'`) it returns a simpler router reading from `serverUrl`.\n- `push` uses **`shallow: true`** so refinements don't re-run `getServerSideProps`, and strips the locale (`stripLocaleFromUrl`) to avoid Next i18n path errors.\n- Hooks `routeChangeComplete` + `beforePopState` to trigger SSR **only** on real page changes (back/forward to a different page), not on every refinement. `beforePopState`/`beforeStart`/`beforeDispose`/`routerOptions` let consumers compose their own logic.\n- `stripLocaleFromUrl` (`src/utils/`) handles i18n locale prefixes in the URL.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch-router-nextjs build\nyarn workspace react-instantsearch-router-nextjs test:e2e   # Playwright (Pages Router)\n```\n\n- Surface is small — most behavior lives in the shared history router (`instantsearch.js`) and react-instantsearch SSR core. Keep this package to the **Next-specific routing glue**.\n- The tricky cases are i18n/locale paths and back/forward navigation deciding whether to re-SSR. e2e in `__tests__/e2e/` (Playwright); see `.claude/rules/e2e.md`.\n\n<!-- File: packages/react-instantsearch/CLAUDE.md (Tokens: ~412 | Category: anthropic-skill) -->\n# react-instantsearch\n\nReact DOM bindings. This package = **hooks (from `react-instantsearch-core`) + UI markup (from `instantsearch-ui-components`)**. The search logic itself is the connector in `instantsearch.js`; don't reimplement it here.\n\n## Layer split (where each thing goes)\n\n- `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts` — the **hook** that wraps the `instantsearch.js` connector. Headless, no DOM. Exported from that package's `src/index.ts`.\n- `packages/react-instantsearch/src/widgets/<Pascal>.tsx` — the **component**: calls the hook, renders UI. Exported from `src/widgets/index.ts`.\n- `packages/react-instantsearch/src/ui/<Pascal>.tsx` — local presentational component, only when the markup isn't already in the shared `instantsearch-ui-components` package.\n\nSo adding/changing a React widget often touches `react-instantsearch-core` (hook) **and** `react-instantsearch` (component) — keep them in sync.\n\n## Working here\n\n```bash\nyarn workspace react-instantsearch build\nyarn workspace react-instantsearch-core build\nyarn jest packages/react-instantsearch/src/widgets/__tests__/<Pascal>   # from repo root\n```\n\n- Uses React 19 / `@testing-library/react` for tests, co-located in `__tests__/`.\n- Cross-flavor behavior lives in `tests/common/widgets/<name>/`; register it in this package's `common-widgets.test.*`.\n- Prefer shared UI from `instantsearch-ui-components` (consume `create<Name>Component` with React's `createElement`) over new local `ui/` components. **Creating or changing a shared component is the `instantsearch-ui-components` package's job** — do that there, then consume it here. Add a local `src/ui/` component only when the markup truly isn't shareable.\n\n<!-- File: packages/vue-instantsearch/CLAUDE.md (Tokens: ~358 | Category: anthropic-skill) -->\n# vue-instantsearch\n\nVue 2 **and** Vue 3 bindings (single source, built for both). Components wrap the `instantsearch.js` connectors — the logic isn't reimplemented here.\n\n## Layout\n\n- `src/components/<Pascal>.vue` (SFC) or `.js` (render function) — the component wrapping a connector. Exported from `src/widgets.js`.\n- `src/mixins/` — shared connector-wiring logic reused across components.\n- To reuse shared markup, import the `create<Name>Component` factory from `instantsearch-ui-components` and render it inside a **`renderCompat`** render function, passing Vue's `h` as `createElement` — e.g. `render: renderCompat((h) => h(createHitsComponent({ createElement: h }), { … }))` (see `src/components/Hits.js`). `renderCompat` (`src/util/vue-compat/`) is the Vue 2/3 render-fn shim. **Creating or changing the shared component itself is the `instantsearch-ui-components` package's job** — do that there, then consume the factory here.\n\n## Working here\n\n```bash\nyarn workspace vue-instantsearch build\nyarn jest packages/vue-instantsearch/src/components/__tests__/<Pascal>   # from repo root\n```\n\n- Vue 2/3 dual support: run `./scripts/prepare-vue3.js` (root `yarn prepare-vue3`) when switching the working tree to Vue 3. Don't assume one Vue version — changes must work for both.\n- Cross-flavor behavior lives in `tests/common/widgets/<name>/`; register it in this package's `common-widgets.test.*`.\n\n<!-- File: .claude/commands/expose-option.md (Tokens: ~860 | Category: command-prompt) -->\n---\ndescription: Thread a new option on an existing connector through all three flavors (JS, React, Vue) + common tests\nargument-hint: <connector> <optionName> [short description of what it does]\n---\n\nYou are exposing a new option through the InstantSearch fan-out. The architecture is **connector (logic) → flavor wrapper → shared UI**, so the option is defined once in the connector and surfaced by each flavor.\n\n**Input:** `$ARGUMENTS`\n\n- First token = the connector (e.g. `connectRefinementList` or just `refinementList`).\n- Second token = the new option name (e.g. `clearOnChange`).\n- Remaining text = a short description of the behavior, if given.\n\nIf the connector or option name is missing or ambiguous, ask before changing files. If the behavior isn't specified, ask what the option should do — do **not** guess at semantics.\n\nThis is a checklist for a cross-flavor option rollout. The connector is the single source of truth; React and Vue only _wrap_ it, so do the connector first, then the wrappers.\n\n## Steps\n\n1. **Scope the contract first.** Read the connector `packages/instantsearch.js/src/connectors/<name>/connect<Pascal>.ts` and its JS widget `packages/instantsearch.js/src/widgets/<name>/<name>.tsx`. Confirm the option doesn't already exist and settle the exact semantics + the option's type signature. This is the contract every flavor must match.\n\n2. **Connector first** (in `packages/instantsearch.js`):\n   - Add the option to the connector's widget-params type with a TSDoc comment.\n   - Implement the behavior in the lifecycle (`init`/`render`/`getWidgetSearchParameters`/etc.).\n   - Surface it on the **typed render state** if consumers need it (untyped render state is an oxlint error).\n   - Thread it through the JS widget. Keep it framework-agnostic — no React/Vue/DOM-framework code in the connector.\n\n   For a presentational-only variant, **reuse the existing connector** with a distinct `$$widgetType` — never fork connector logic.\n\n3. **If the option changes shared markup/layout** (not just behavior), update the shared `create<Name>Component` in `packages/instantsearch-ui-components` _before_ wiring the flavors — its output is the contract they consume.\n\n4. **Thread it through the wrappers** (independent of each other once the connector is settled):\n   - **React** — the hook `packages/react-instantsearch-core/src/connectors/use<Pascal>.ts` and the component `packages/react-instantsearch/src/widgets/<Pascal>.tsx`, kept in sync and typed.\n   - **Vue** — `packages/vue-instantsearch/src/components/<Pascal>.{vue,js}` (and `src/mixins/` if wiring lives there), working for **both Vue 2 and Vue 3**.\n\n5. **Tests.** Cross-flavor behavior → add/extend `tests/common/connectors/<name>/` (or `.../widgets/<name>/`) and register it in each flavor's `common-*.test.*` (see `tests/common/README.md`); flavor-specific assertions stay in each package's co-located `__tests__/`. If browser-level behavior is affected, add an e2e spec (see `.claude/rules/e2e.md`).\n\n6. **Verify.** Confirm all three flavors expose the same option with consistent naming/types, then:\n   ```bash\n   yarn jest packages/instantsearch.js/src/connectors/<name>\n   yarn jest <the react/vue paths you touched>\n   yarn type-check\n   yarn lint:changed\n   ```\n   (Or run `/preflight`.)\n\n## Notes\n\n- Why this order: the connector is the contract; React and Vue only _wrap_ it, so they're independent of each other and safe to do once the connector is settled.\n- Don't hand-edit changelogs (Ship.js generates them). Commit message: `feat(<widget>): add <optionName> option`.\n\n<!-- File: .claude/commands/preflight.md (Tokens: ~545 | Category: command-prompt) -->\n---\ndescription: Run the pre-push checklist — lint, types, relevant tests, and a Conventional-Commit sanity check\nallowed-tools: Bash(yarn lint:changed), Bash(yarn lint:fix), Bash(yarn type-check), Bash(yarn type-check:*), Bash(yarn jest:*), Bash(git status:*), Bash(git diff:*), Bash(git log:*)\n---\n\nRun the InstantSearch pre-push checks against the **current changes** and report a concise pass/fail summary. Don't fix anything silently — surface failures and propose fixes.\n\n## Steps\n\n1. **Scope the change.** `git status` + `git diff --name-only` (and vs. the base branch if on a feature branch) to see which packages/files are touched.\n\n2. **Lint the changed files:** `yarn lint:changed`. If it fails, show the violations; offer `yarn lint:fix` for auto-fixable ones (note that oxlint bans `for-in`/`for-of`/`async` and implicit `any` in library code — those need manual fixes).\n\n3. **Type-check:** `yarn type-check`. If any touched code interacts with the legacy algoliasearch versions, also run `yarn type-check:v3` / `yarn type-check:v4`.\n\n4. **Run the relevant tests** — not the whole suite. Map changed files to their `yarn jest <path>` (e.g. a connector → `yarn jest packages/instantsearch.js/src/connectors/<name>`; a React widget → its `__tests__` path). If a connector's cross-flavor behavior changed, include the matching `tests/common/` path.\n\n5. **Conventional-Commit sanity check.** Look at staged changes / recent commits and confirm the message fits `type(scope): description` (scope = widget/connector or topic like `deps`/`ci`). Flag if it doesn't; suggest a corrected message. Reference issues with `fix #1234` in the body when applicable.\n\n## Output\n\nReport a short checklist:\n\n- ✅/❌ lint:changed\n- ✅/❌ type-check (+ v3/v4 if run)\n- ✅/❌ tests (list the paths run)\n- ✅/❌ commit message\n\nFor any ❌, show the failing output and the smallest fix. Don't run the full `yarn test` or `yarn build` unless asked — this is the fast pre-push loop.\n\nRun the checks directly. If a check fails, fix it in the package that owns the failing file before re-running.\n\n<!-- File: packages/algoliasearch-helper/examples/readme.md (Tokens: ~22 | Category: plugin-manifest) -->\nThe examples moved to [codepen](http://codepen.io/collection/nwWJbe/).\n\n<!-- File: packages/algoliasearch-helper/README.md (Tokens: ~12414 | Category: plugin-manifest) -->\n_Coming from V1 (or js client v2)?_ Read the [migration guide](https://github.com/algolia/algoliasearch-helper-js/wiki/Migration-guide-:-V1-to-V2) to the new version of the Helper.\n\n_Coming from V2?_ Read the [migration guide](https://github.com/algolia/algoliasearch-helper-js/wiki/Migration-guide-:-V2-to-V3) to the new version of the Helper.\n\n**The JavaScript helper is an advanced library we provide to our users. If you are looking to build a complete search interface, we recommend you to use [InstantSearch](https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/). If you want to build an autocomplete menu, see [Autocomplete](https://github.com/algolia/autocomplete).**\n\n# algoliasearch-helper\n\nThis module is the companion of the [algolia/algoliasearch-client-javascript](https://github.com/algolia/algoliasearch-client-javascript). It helps you keep track of the search parameters and provides a higher level API.\n\n[See the helper in action](https://community.algolia.com/algoliasearch-helper-js/)\n\n[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Downloads][downloads-image]][downloads-url] [![jsDelivr Hits][jsdelivr-badge]][jsdelivr-hits]\n\n[license-image]: http://img.shields.io/badge/license-MIT-green.svg?style=flat-square\n[license-url]: LICENSE\n[downloads-image]: https://img.shields.io/npm/dm/algoliasearch-helper.svg?style=flat-square\n[downloads-url]: http://npm-stat.com/charts.html?package=algoliasearch-helper\n[version-svg]: https://img.shields.io/npm/v/algoliasearch-helper.svg?style=flat-square\n[package-url]: https://npmjs.org/package/algoliasearch-helper\n[jsdelivr-badge]: https://data.jsdelivr.com/v1/package/npm/algoliasearch-helper/badge\n[jsdelivr-hits]: https://www.jsdelivr.com/package/npm/algoliasearch-helper\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Features](#features)\n- [Examples](#examples)\n  - [Vanilla JavaScript](#vanilla-javascript)\n  - [AngularJS module](#angularjs-module)\n- [Helper cheatsheet](#helper-cheatsheet)\n  - [Add the helper in your project](#add-the-helper-in-your-project)\n  - [Regular `<script>` tag](#regular-script-tag)\n  - [With npm](#with-npm)\n  - [Init the helper](#init-the-helper)\n  - [Helper lifecycle](#helper-lifecycle)\n  - [Objects](#objects)\n  - [Search](#search)\n  - [Events](#events)\n  - [Query](#query)\n  - [Filtering results](#filtering-results)\n  - [Facet utilities](#facet-utilities)\n  - [Tags](#tags)\n  - [Pagination](#pagination)\n  - [Index](#index)\n  - [One time query](#one-time-query)\n  - [Query parameters](#query-parameters)\n  - [Results format](#results-format)\n- [Browser support](#browser-support)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Features\n\n- Search parameters management\n- Facets exclusions\n- Pagination\n- Disjunctive faceting (search on two or more values of the same facet)\n\n## Examples\n\n### Vanilla JavaScript\n\nA small example that uses Browserify to manage modules.\n\n```js\nvar algoliasearch = require('algoliasearch');\nvar algoliasearchHelper = require('algoliasearch-helper');\n\nvar client = algoliasearch('appId', 'apiKey');\n\nvar helper = algoliasearchHelper(client, 'indexName', {\n  facets: ['mainCharacterFirstName', 'year'],\n  disjunctiveFacets: ['director'],\n});\n\nhelper.on('result', function (event) {\n  console.log(event.results);\n});\n\nhelper.addDisjunctiveFacetRefinement('director', 'Clint Eastwood');\nhelper.addDisjunctiveFacetRefinement('director', 'Sofia Coppola');\n\nhelper.addNumericRefinement('year', '=', 2003);\n\n// Search for any movie filmed in 2003 and directed by either C. Eastwood or S. Coppola\nhelper.search();\n```\n\nSee more examples in the [examples folder](examples/)\n\n### AngularJS module\n\n```js\n<script src=\"https://cdn.jsdelivr.net/algoliasearch/3/algoliasearch.angular.js\"></script>\n<script src=\"dist/algoliasearch.helper.min.js\"></script>\n\n<script type=\"text/javascript\">\nangular.module('searchApp', ['ngSanitize', 'algoliasearch'])\n.controller('searchController', ['$scope', '$sce', 'algolia', function($scope, $sce, algolia) {\n  var algolia = algolia.Client('applicationId', 'apiKey');\n  $scope.q = '';\n  $scope.content = null;\n  $scope.helper = algoliasearchHelper(algolia, 'indexName', {\n    facets: ['type', 'shipping'],\n    disjunctiveFacets: ['category', 'manufacturer'],\n    hitsPerPage: 5,\n  });\n  $scope.helper.on('result', function(event) {\n    $scope.$apply(function() {\n      $scope.content = event.results;\n    });\n  });\n  $scope.toggleRefine = function($event, facet, value) {\n    $event.preventDefault();\n    $scope.helper.toggleRefine(facet, value).search();\n  };\n  $scope.$watch('q', function(q) {\n    $scope.helper.setQuery(q).search();\n  });\n  $scope.helper.search();\n}]);\n</script>\n```\n\nYou can see the full [Angular example here](https://codepen.io/Algolia/pen/PzZobK)\n\n## Helper cheatsheet\n\n[There is also a complete JSDoc](https://community.algolia.com/algoliasearch-helper-js/reference.html)\n\n### Add the helper in your project\n\n### Regular `<script>` tag\n\nUse our [jsDelivr](http://www.jsdelivr.com/) build:\n\n`<script src=\"https://cdn.jsdelivr.net/algoliasearch.helper/2/algoliasearch.helper.min.js\"></script>`\n\n### With npm\n\n`npm install algoliasearch-helper`\n\n### Init the helper\n\n```js\nvar helper = algoliasearchHelper(client, 'indexName' /*, parameters*/);\n```\n\n### Helper lifecycle\n\n1. modify the parameters of the search (usually through user interactions)<br/> `helper.setQuery('iphone').addFacetRefinement('category', 'phone')`\n\n2. trigger the search (after all the modification have been applied)<br/> `helper.search()`\n\n3. read the results (with the event \"result\" handler) and update the UI with the results<br/> `helper.on('result', function(event) { updateUI(event.results); });`\n\n4. go back to 1\n\n### Objects\n\n**AlgoliasearchHelper**: the helper. Keeps the state of the search, makes the queries and calls the handlers when an event happen.\n\n**SearchParameters**: the object representing the state of the search. The current state is stored in `helperInstance.state`.\n\n**SearchResults**: the object in which the Algolia answers are transformed into. This object is passed to the result event handler. An example of SearchResults in JSON is available at [the end of this readme](#results-format)\n\n### Search\n\nThe search is triggered by the `search()` method.\n\nIt takes all the previous modifications to the search and uses them to create the queries to Algolia. The search parameters are immutable.\n\nExample:\n\n```js\nvar helper = algoliasearchHelper(client, indexName);\n\n// Let's monitor the results with the console\nhelper.on('result', function (event) {\n  console.log(event.results);\n});\n\n// Let's make an empty search\n// The results are all sorted using the dashboard configuration\nhelper.search();\n\n// Let's search for \"landscape\"\nhelper.setQuery('landscape').search();\n\n// Let's add a category \"photo\"\n// Will make a search with \"photo\" tag and \"landscape\" as the query\nhelper.addTag('photo').search();\n```\n\n### Events\n\nThe helper is a Node.js [EventEmitter](https://nodejs.org/api/events.html#events_class_events_eventemitter) instance.\n\n`result`: get notified when new results are received. The handler function will receive two objects (`SearchResults` and `SearchParameters`).\n\n`error`: get notified when errors are received from the API.\n\n`change`: get notified when a property has changed in the helper\n\n`search` : get notified when a request is sent to Algolia\n\n#### Listen to the `result` event\n\n```js\nhelper.on('result', updateTheResults);\n```\n\n#### Listen to a `result` event once\n\n```js\nhelper.once('result', updateTheResults);\n```\n\n#### Remove a `result` listener\n\n```js\nhelper.removeListener('result', updateTheResults);\n```\n\n#### Remove all `result` listeners\n\n```js\nhelper.removeAllListeners('result');\n```\n\nAll the methods from Node.js [EventEmitter](https://nodejs.org/api/events.html#events_class_events_eventemitter) class are available.\n\n### Query\n\n#### Do a search with the query \"fruit\"\n\n```javscript\nhelper.setQuery('fruit').search();\n```\n\n### Filtering results\n\nFacets are filters to retrieve a subset of an index having a specific value for a given attribute. First you need to define which attribute will be used as a facet in the dashboard: [https://www.algolia.com/explorer#?tab=display](https://www.algolia.com/explorer#?tab=display)\n\n#### Regular (conjunctive) facets\n\nRefinements are ANDed by default (Conjunctive selection).\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  facets: ['ANDFacet'],\n});\n```\n\n##### Add a facet filter\n\n```js\nhelper.addFacetRefinement('ANDFacet', 'valueOfANDFacet').search();\n```\n\n##### Remove a facet filter\n\n```js\nhelper.removeFacetRefinement('ANDFacet', 'valueOfANDFacet').search();\n```\n\n#### Disjunctive facets\n\nRefinements are ORed by default (Disjunctive selection).\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  disjunctiveFacets: ['ORFacet'],\n});\n```\n\n##### Add a facet filter\n\n```js\nhelper.addDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();\n```\n\n##### Remove a facet filter\n\n```js\nhelper.removeDisjunctiveFacetRefinement('ORFacet', 'valueOfORFacet').search();\n```\n\n#### Negative facets\n\nFilter so that we do NOT get a given facet\n\n##### Facet definition (same as \"AND\" facet)\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  facets: ['ANDFacet'],\n}).search();\n```\n\n##### Exclude a value for a facet\n\n```js\nhelper.addFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');\n```\n\n##### Remove an exclude from the list of excluded values\n\n```js\nhelper.removeFacetExclusion('ANDFacet', 'valueOfANDFacetToExclude');\n```\n\n#### Numeric facets\n\nFilter over numeric attributes with math operations like `=`, `>`, `<`, `>=`, `<=`. Can be used for numbers and dates (if converted to timestamp)\n\n##### Facet definition\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  disjunctiveFacets: ['numericAttribute'],\n});\n```\n\n##### Add numeric refinements\n\n```js\nhelper.addNumericRefinement('numericAttribute', '=', '3').search();\n// filter to only the results that match numericAttribute=3\nhelper.addNumericRefinement('numericAttribute', '=', '4').search();\n// filter to only the results that match numericAttribute=3 AND numericAttribute=4\n\n// On another numeric with no previous filter\nhelper\n  .addNumericRefinement('numericAttribute2', '=', ['42', '56', '37'])\n  .search();\n// filter to only the results that match numericAttribute2=42 OR numericAttribute2=56 OR numericAttribute2=37\n```\n\n##### Remove a numeric refinement\n\n```js\nhelper.removeNumericRefinement('numericAttribute', '=', '3').search();\n```\n\n##### Batch numeric filter removal\n\n```js\n// for the single operator = on numericAttribute\nhelper.removeNumericRefinement('numericAttribute', '=').search();\n// for all the refinements on numericAttribute\nhelper.removeNumericRefinement('numericAttribute').search();\n```\n\n#### Hierarchical facets\n\nHierarchical facets are useful to build such navigation menus:\n\n```txt\n| products\n  > fruits\n    > citrus\n    | strawberries\n    | peaches\n    | apples\n```\n\nHere, we refined the search this way:\n\n- click on fruits\n- click on citrus\n\n##### Usage\n\nTo build such menu, you need to use hierarchical faceting:\n\n```javascript\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n    },\n  ],\n});\n```\n\n**Requirements:** All the specified `attributes` must be defined in your Algolia settings as attributes for faceting.\n\nGiven your objects looks like this:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": \"fruits\",\n    \"lvl1\": \"fruits > citrus\"\n  }\n}\n```\n\nAnd you refine `products`:\n\n```js\nhelper.toggleFacetRefinement('products', 'fruits > citrus');\n```\n\nYou will get a hierarchical presentation of your facet values: a navigation menu of your facet values.\n\n```js\nhelper.on('result', function (event) {\n  console.log(event.results.hierarchicalFacets[0]);\n  // {\n  //   'name': 'products',\n  //   'count': null,\n  //   'isRefined': true,\n  //   'path': null,\n  //   'data': [{\n  //     'name': 'fruits',\n  //     'path': 'fruits',\n  //     'count': 1,\n  //     'isRefined': true,\n  //     'data': [{\n  //       'name': 'citrus',\n  //       'path': 'fruits > citrus',\n  //       'count': 1,\n  //       'isRefined': true,\n  //       'data': null\n  //     }]\n  //   }]\n  // }\n});\n```\n\nTo ease navigation, we always:\n\n- provide the root level categories\n- provide the current refinement sub categories (`fruits > citrus > *`: n + 1)\n- provide the parent refinement (`fruits > citrus` => `fruits`: n -1) categories\n- refine the search using the current hierarchical refinement\n\n##### Multiple values per level\n\nYour records can also share multiple categories between one another by using arrays inside your object:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": [\"fruits\", \"color\"],\n    \"lvl1\": [\"fruits > citrus\", \"color > orange\"]\n  }\n},\n{\n  \"objectID\": \"456\",\n  \"name\": \"grapefruit\",\n  \"categories\": {\n    \"lvl0\": [\"fruits\", \"color\", \"new\"],\n    \"lvl1\": [\"fruits > citrus\", \"color > yellow\", \"new > citrus\"]\n  }\n}\n```\n\n##### Specifying another separator\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      separator: '|',\n    },\n  ],\n});\n\nhelper.toggleFacetRefinement('products', 'fruits|citrus');\n```\n\nWould mean that your objects look like so:\n\n```json\n{\n  \"objectID\": \"123\",\n  \"name\": \"orange\",\n  \"categories\": {\n    \"lvl0\": \"fruits\",\n    \"lvl1\": \"fruits|citrus\"\n  }\n}\n```\n\n##### Specifying a different sort order for values\n\nThe default sort for the hierarchical facet view is: `isRefined:desc (first show refined), name:asc (then sort by name)`.\n\nYou can specify a different sort order by using:\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      sortBy: ['count:desc', 'name:asc'], // first show the most common values, then sort by name\n    },\n  ],\n});\n```\n\nThe available sort tokens are:\n\n- count\n- isRefined\n- name\n- path\n\n##### Restrict results and hierarchical values to non-root level\n\nLet's say you have a lot of levels:\n\n```\n- fruits\n  - yellow\n    - citrus\n      - spicy\n```\n\nBut you only want to get the values starting at \"citrus\", you can use `rootPath`\n\nYou can specify an root path to filter the hierarchical values\n\n```\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [{\n    name: 'products',\n    attributes: ['categories.lvl0', 'categories.lvl1', 'categories.lvl2', 'categories.lvl3'],\n    rootPath: 'fruits > yellow > citrus'\n  }]\n});\n```\n\nHaving a rootPath will refine the results on it **automatically**.\n\n##### Hide parent level of current parent level\n\nBy default the hierarchical facet is going to return the child and parent facet values of the current refinement.\n\nIf you do not want to get the parent facet values you can set showParentLevel to false\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      showParentLevel: false,\n    },\n  ],\n});\n```\n\n##### Asking for the current breadcrumb\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hierarchicalFacets: [\n    {\n      name: 'products',\n      attributes: ['categories.lvl0', 'categories.lvl1'],\n      separator: '|',\n    },\n  ],\n});\n\nhelper.toggleFacetRefinement('products', 'fruits|citrus');\nvar breadcrumb = helper.getHierarchicalFacetBreadcrumb('products');\n\nconsole.log(breadcrumb);\n// ['fruits', 'citrus']\n\nconsole.log(breadcrumb.join(' | '));\n// 'fruits | citrus'\n```\n\n#### Clearing filters\n\n##### Clear all the refinements for all the refined attributes\n\n```js\nhelper.clearRefinements().search();\n```\n\n##### Clear all the refinements for a specific attribute\n\n```js\nhelper.clearRefinements('ANDFacet').search();\n```\n\n##### [ADVANCED] Clear only the exclusions on the \"ANDFacet\" attribute\n\n```js\nhelper\n  .clearRefinements(function (value, attribute, type) {\n    return type === 'exclude' && attribute === 'ANDFacet';\n  })\n  .search();\n```\n\n### Facet utilities\n\n#### Get the values of a facet with the default sort\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetValues('age');\n  // It will be ordered :\n  //  - refined facets first\n  //  - then ordered by number of occurence (bigger count -> higher in the list)\n  //  - then ordered by name (alphabetically)\n});\n```\n\n#### Get the values of a facet with a custom sort\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetValues('age', { sortBy: ['count:asc'] });\n  // It will be ordered by number of occurence (lower number => higher position)\n  // Elements that can be sorted : count, name, isRefined\n  // Type of sort : 'asc' for ascending order, 'desc' for descending order\n});\n```\n\n#### Get the facet stats\n\n_This only apply on numeric based facets/attributes._\n\n```js\nhelper.on('result', function (event) {\n  // Get the facet values for the attribute age\n  event.results.getFacetStats('age');\n});\n```\n\n### Tags\n\nTags are an easy way to do filtering. They are based on a special attribute in the records named `_tags`, which can be a single string value or an array of strings.\n\n#### Add a tag filter for the value \"landscape\"\n\n```js\nhelper.addTag('landscape').search();\n```\n\n#### Remove a tag filter for the value \"landscape\"\n\n```js\nhelper.removeTag('landscape').search();\n```\n\n#### Clear all the tags filters\n\n```js\nhelper.clearTags().search();\n```\n\n### Pagination\n\n#### Get the current page\n\n```js\nhelper.getPage();\n```\n\n#### Change page\n\n```js\nhelper.setPage(3).search();\n```\n\n#### Automatic reset to page 0\n\nDuring a search, changing the parameters will update the result set, which can then change the number of pages in the result set. Therefore, the behavior has been standardized so that any operation that may change the number of page will reset the pagination to page 0.\n\nThis may lead to some unexpected behavior. For example:\n\n```js\nhelper.setPage(4);\nhelper.getPage(); // 4\nhelper.setQuery('foo');\nhelper.getPage(); // 0\n```\n\nNon exhaustive list of operations that trigger a reset:\n\n- refinements (conjunctive, exclude, disjunctive, hierarchical, numeric)\n- tags\n- index (setIndex)\n- setQuery\n- setHitsPerPage\n- setTypoTolerance\n\n### Index\n\nIndex can be changed. The common use case is when you have several slaves with different sort order (sort by relevance, price or any other attribute).\n\n#### Change the current index\n\n```js\nhelper.setIndex('index_orderByPrice').search();\n```\n\n#### Get the current index\n\n```js\nvar currentIndex = helper.getIndex();\n```\n\n### One time query\n\nSometime it's convenient to reuse the current search parameters with small changes without changing the state stored in the helper. That's why there is a function called `searchOnce`. This method does not trigger `change` or `error` events.\n\nIn the following, we are using `searchOnce` to fetch only a single element using all the other parameters already set in the search parameters.\n\n#### Using searchOnce with a callback\n\n```js\nvar state = helper.searchOnce(\n  { hitsPerPage: 1 },\n  function (error, content, state) {\n    // if an error occured it will be passed in error, otherwise its value is null\n    // content contains the results formatted as a SearchResults\n    // state is the instance of SearchParameters used for this search\n  }\n);\n```\n\n#### Using searchOnce with a promise\n\n```js\nvar state1 = helper.searchOnce({ hitsPerPage: 1 }).then(function (res) {\n  // res contains\n  // {\n  //   content : SearchResults\n  //   state : SearchParameters (the one used for this specific search)\n  // }\n});\n```\n\n### Query parameters\n\nThere are lots of other parameters you can set.\n\n#### Set a parameter at the initialization of the helper\n\n```js\nvar helper = algoliasearchHelper(client, indexName, {\n  hitsPerPage: 50,\n});\n```\n\n#### Set a parameter later\n\n```js\nhelper.setQueryParameter('hitsPerPage', 20).search();\n```\n\n#### List of parameters that can be set\n\n<table cellspacing=\"0\" cellpadding=\"0\" class=\"params\">\n  <tbody>\n    <tr>\n      <td valign=\"top\" class=\"td1\">\n        <p class=\"p1\"><span class=\"s1\"><b>Name</b></span></p>\n      </td>\n      <td valign=\"top\" class=\"td2\">\n        <p class=\"p1\"><span class=\"s1\"><b>Type</b></span></p>\n      </td>\n      <td valign=\"top\" class=\"td3\">\n        <p class=\"p1\"><span class=\"s1\"><b>Description</b></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">advancedSyntax</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the advanced syntax.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/advancedSyntax/\">advancedSyntax on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">allowTyposOnNumericTokens</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the engine allow typos on numerics.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/allowTyposOnNumericTokens/\">allowTyposOnNumericTokens on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">analytics</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the analytics</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/analytics/\">analytics on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">analyticsTags</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Tag of the query in the analytics.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/analyticsTags/\">analyticsTags on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundLatLng</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Center of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLng/\">aroundLatLng on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundLatLngViaIP</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Center of the search, retrieve from the user IP.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundLatLngViaIP/\">aroundLatLngViaIP on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundPrecision</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Precision of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundPrecision/\">aroundPrecision on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">aroundRadius</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Radius of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/aroundRadius/\">aroundRadius on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minimumAroundRadius</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Minimum radius of the geo search.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minimumAroundRadius/\">minimumAroundRadius on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToHighlight</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to highlight</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToHighlight/\">attributesToHighlight on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToRetrieve</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to retrieve</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToRetrieve/\">attributesToRetrieve on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">attributesToSnippet</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">List of attributes to snippet</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/attributesToSnippet/\">attributesToSnippet on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">disjunctiveFacets</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">Array.&lt;string&gt;</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">All the declared disjunctive facets</span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">distinct</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean|number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Remove duplicates based on the index setting attributeForDistinct</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/distinct/\">distinct on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">facets</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">Array.&lt;string&gt;</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">All the facets that will be requested to the server</span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">filters</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p1\"><span class=\"s1\">Add filters to the query (similar to WHERE clauses)</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/filters/\">filters on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">getRankingInfo</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">integer</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the ranking informations in the response</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/getRankingInfo/\">getRankingInfo on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">hitsPerPage</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of hits to be returned by the search API</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/hitsPerPage/\">hitsPerPage on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">ignorePlurals</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the plurals be ignored</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/ignorePlurals/\">ignorePlurals on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">insideBoundingBox</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Geo search inside a box.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/insideBoundingBox/\">insideBoundingBox on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">insidePolygon</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Geo search inside a polygon.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/insidePolygon/\">insidePolygon on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">maxValuesPerFacet</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of values for each facetted attribute</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/maxValuesPerFacet/\">maxValuesPerFacet on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minWordSizefor1Typo</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of characters to wait before doing one character replacement.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minWordSizefor1Typo/\">minWordSizefor1Typo on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">minWordSizefor2Typos</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Number of characters to wait before doing a second character replacement.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/minWordSizefor2Typos/\">minWordSizefor2Typos on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">optionalWords</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Add some optional words to those defined in the dashboard</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/optionalWords/\">optionalWords on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">page</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">number</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">The current page number</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"algolia.com/doc/api-reference/api-parameters/page/\">page on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">query</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Query string of the instant search. The empty string is a valid query.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/query/\">query on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">queryType</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">How the query should be treated by the search engine. Possible values: prefixAll, prefixLast, prefixNone</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/queryType/\">queryType on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">removeWordsIfNoResults</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Possible values are \"lastWords\" \"firstWords\" \"allOptional\" \"none\" (default)</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/removeWordsIfNoResults/\">removeWordsIfNoResults on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">replaceSynonymsInHighlight</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Should the engine replace the synonyms in the highlighted results.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/replaceSynonymsInHighlight/\">replaceSynonymsInHighlight on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">restrictSearchableAttributes</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Restrict which attribute is searched.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/restrictSearchableAttributes/\">restrictSearchableAttributes on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">synonyms</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">boolean</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Enable the synonyms</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/synonyms/\">synonyms on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">tagFilters</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">Contains the tag filters in the raw format of the Algolia API. Setting this parameter is not compatible with the of the add/remove/toggle methods of the tag api.</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/tagFilters/\">tagFilters on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n    <tr>\n      <td valign=\"top\" class=\"td4\">\n        <p class=\"p2\"><span class=\"s2\">typoTolerance</span></p>\n      </td>\n      <td valign=\"top\" class=\"td5\">\n        <p class=\"p3\"><span class=\"s1\">string</span></p>\n      </td>\n      <td valign=\"top\" class=\"td6\">\n        <p class=\"p4\"><span class=\"s1\">How the typo tolerance behave in the search engine. Possible values: true, false, min, strict</span></p>\n        <p class=\"p5\"><span class=\"s1\"><a href=\"https://www.algolia.com/doc/api-reference/api-parameters/typoTolerance/\">typoTolerance on Algolia.com<span class=\"s3\"></span></a></span></p>\n      </td>\n    </tr>\n  </tbody>\n</table>\n\n### Results format\n\nHere is an example of a result object you get with the `result` event.\n\n```js\n{\n   \"hitsPerPage\": 10,\n   \"processingTimeMS\": 2,\n   \"facets\": [\n      {\n         \"name\": \"type\",\n         \"data\": {\n            \"HardGood\": 6627,\n            \"BlackTie\": 550,\n            \"Music\": 665,\n            \"Software\": 131,\n            \"Game\": 456,\n            \"Movie\": 1571\n         },\n         \"exhaustive\": false\n      },\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"Free shipping\": 5507\n         },\n         \"name\": \"shipping\"\n      }\n   ],\n   \"hits\": [\n      {\n         \"thumbnailImage\": \"http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_54x108_s.gif\",\n         \"_highlightResult\": {\n            \"shortDescription\": {\n               \"matchLevel\": \"none\",\n               \"value\": \"Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection\",\n               \"matchedWords\": []\n            },\n            \"category\": {\n               \"matchLevel\": \"none\",\n               \"value\": \"Computer Security Software\",\n               \"matchedWords\": []\n            },\n            \"manufacturer\": {\n               \"matchedWords\": [],\n               \"value\": \"Webroot\",\n               \"matchLevel\": \"none\"\n            },\n            \"name\": {\n               \"value\": \"Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows\",\n               \"matchedWords\": [],\n               \"matchLevel\": \"none\"\n            }\n         },\n         \"image\": \"http://img.bbystatic.com/BestBuy_US/images/products/1688/1688832_105x210_sc.jpg\",\n         \"shipping\": \"Free shipping\",\n         \"bestSellingRank\": 4,\n         \"shortDescription\": \"Safeguard your PC, Mac, Android and iOS devices with comprehensive Internet protection\",\n         \"url\": \"http://www.bestbuy.com/site/webroot-secureanywhere-internet-security-3-devi…d=1219060687969&skuId=1688832&cmp=RMX&ky=2d3GfEmNIzjA0vkzveHdZEBgpPCyMnLTJ\",\n         \"name\": \"Webroot SecureAnywhere Internet Security (3-Device) (1-Year Subscription) - Mac/Windows\",\n         \"category\": \"Computer Security Software\",\n         \"salePrice_range\": \"1 - 50\",\n         \"objectID\": \"1688832\",\n         \"type\": \"Software\",\n         \"customerReviewCount\": 5980,\n         \"salePrice\": 49.99,\n         \"manufacturer\": \"Webroot\"\n      },\n      ....\n   ],\n   \"nbHits\": 10000,\n   \"disjunctiveFacets\": [\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"5\": 183,\n            \"12\": 112,\n            \"7\": 149,\n            ...\n         },\n         \"name\": \"customerReviewCount\",\n         \"stats\": {\n            \"max\": 7461,\n            \"avg\": 157.939,\n            \"min\": 1\n         }\n      },\n      {\n         \"data\": {\n            \"Printer Ink\": 142,\n            \"Wireless Speakers\": 60,\n            \"Point & Shoot Cameras\": 48,\n            ...\n         },\n         \"name\": \"category\",\n         \"exhaustive\": false\n      },\n      {\n         \"exhaustive\": false,\n         \"data\": {\n            \"> 5000\": 2,\n            \"1 - 50\": 6524,\n            \"501 - 2000\": 566,\n            \"201 - 500\": 1501,\n            \"101 - 200\": 1360,\n            \"2001 - 5000\": 47\n         },\n         \"name\": \"salePrice_range\"\n      },\n      {\n         \"data\": {\n            \"Dynex™\": 202,\n            \"Insignia™\": 230,\n            \"PNY\": 72,\n            ...\n         },\n         \"name\": \"manufacturer\",\n         \"exhaustive\": false\n      }\n   ],\n   \"query\": \"\",\n   \"nbPages\": 100,\n   \"page\": 0,\n   \"index\": \"bestbuy\"\n}\n```\n\n## Browser support\n\nThis project works on any [ES5](https://en.wikipedia.org/wiki/ECMAScript#5th_Edition) browser, basically >= IE9+.\n\n<!-- File: packages/create-instantsearch-app/README.md (Tokens: ~2263 | Category: plugin-manifest) -->\n# Create InstantSearch App\n\n> ⚡️ Build InstantSearch apps at the speed of thought.\n\n[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Build Status][ci-svg]][ci-url]\n\n`create-instantsearch-app` is a command line utility that helps you quick start your InstantSearch app using any [Algolia][algolia-website] InstantSearch flavor ([InstantSearch.js][instantsearchjs-github], [React InstantSearch][react-instantsearch-github], [Vue InstantSearch][vue-instantsearch-github], [InstantSearch iOS][instantsearch-ios-github] and [InstantSearch Android][instantsearch-android-github]).\n\n<p align=\"center\">\n  <img src=\"preview.png\" width=\"800\" alt=\"Preview\">\n</p>\n\n<details>\n  <summary><strong>Contents</strong></summary>\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Get started](#get-started)\n- [Usage](#usage)\n- [API](#api)\n- [Tutorials](#tutorials)\n- [Previews](#previews)\n- [Contributing](#contributing)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n</details>\n\n## Get started\n\n> The tool requires Node ≥ 8.\n\n```\nnpx create-instantsearch-app my-app\ncd my-app\nnpm start\n```\n\n> [`npx`](https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b) is a tool introduced in `npm@5.2.0` that makes it possible to run CLI tools hosted on the npm registry.\n\n---\n\nAlternatively, you can use [Yarn](https://yarnpkg.com):\n\n```\nyarn create instantsearch-app my-app\ncd my-app\nyarn start\n```\n\n> **Important** Selecting 'Y' when being prompted to ‘Enable user events’ activates the [`insights`](https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/#widget-param-insights) option for compatible templates, allowing Algolia to process your user Events. Events can unlock powerful features, enhancing your application's effectiveness and we encourage you to consider enabling this valuable functionality. Please review our [API reference](https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/#widget-param-insights) for more details about Events collection and settings.\n\n## Usage\n\nThis package comes with the module `createInstantSearchApp(path, options?)` and the command-line tool `create-instantsearch-app`.\n\n```\n$ create-instantsearch-app --help\n\n  Usage: create-instantsearch-app <project-directory> [options]\n\n  Options:\n\n    -v, --version                                      output the version number\n    --name <name>                                      The name of the application\n    --app-id <appId>                                   The application ID\n    --api-key <apiKey>                                 The Algolia search API key\n    --index-name <indexName>                           The main index of your search\n    --attributes-to-display <attributesToDisplay>      The attributes of your index to display\n    --image-attribute <imageAttribute>                 The attribute of your index to use for image display\n    --attributes-for-faceting <attributesForFaceting>  The attributes for faceting\n    --template <template>                              The InstantSearch template to use\n    --library-version <libraryVersion>                 The version of the library\n    --config <config>                                  The configuration file to get the options from\n    --no-installation                                  Ignore dependency installation\n    --no-interactive                                   Do not ask any interactive questions\n    -h, --help                                         output usage information\n```\n\n#### `--template`\n\nSupported templates are:\n\n- [`InstantSearch.js`][instantsearchjs-github]\n- [`React InstantSearch`][react-instantsearch-github]\n- [`React InstantSearch Native`][react-instantsearch-github]\n- [`Vue InstantSearch`][vue-instantsearch-github]\n- [`InstantSearch iOS`][instantsearch-ios-github]\n- [`InstantSearch Android`][instantsearch-android-github]\n\n```\ncreate-instantsearch-app my-app --template \"React InstantSearch\"\n```\n\nYou can also [create your own template](docs/custom-templates.md) and specify its path.\n\n#### `--config`\n\nThe `config` flag is handy to automate app generations.\n\n<h6 align=\"center\">config.json</h6>\n\n```json\n{\n  \"name\": \"my-app\",\n  \"template\": \"InstantSearch.js\",\n  \"libraryVersion\": \"2.8.0\",\n  \"appId\": \"MY_APP_ID\",\n  \"apiKey\": \"MY_API_KEY\",\n  \"indexName\": \"MY_INDEX_NAME\",\n  \"searchPlaceholder\": \"Search\",\n  \"attributesToDisplay\": [\"name\", \"description\"],\n  \"imageAttribute\": \"image\",\n  \"attributesForFaceting\": [\"brand\", \"location\"],\n  \"enableInsights\": true\n}\n```\n\nCreate the app based on this configuration:\n\n```\ncreate-instantsearch-app my-app --config config.json\n```\n\n## API\n\n`create-instantsearch-app` is based on the module `createInstantSearchApp(path, options?)`. The same [camel cased](https://en.wikipedia.org/wiki/Camel_case) options as the CLI are available.\n\n```javascript\nconst createInstantSearchApp = require('create-instantsearch-app');\n\nconst app = createInstantSearchApp('~/lab/my-app', {\n  template: 'InstantSearch.js',\n  libraryVersion: '2.0.0',\n  attributesToDisplay: ['name', 'description'],\n  imageAttribute: 'image',\n  attributesForFaceting: ['keywords'],\n  enableInsights: true,\n});\n\napp.create().then(() => console.log('App generated!'));\n```\n\n## Tutorials\n\n- [Creating a custom template](docs/custom-templates.md)\n- [Deploying an InstantSearch App](docs/deploy.md)\n\n## Previews\n\nYou can use the web templates on CodeSandbox:\n\n- [InstantSearch.js](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/instantsearch.js)\n- [React InstantSearch](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/react-instantsearch)\n- [Vue InstantSearch](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/vue-instantsearch)\n- [Autocomplete.js](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/autocomplete.js)\n- [JavaScript Client](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/javascript-client)\n- [JavaScript Helper](https://codesandbox.io/s/github/algolia/instantsearch/tree/templates/javascript-helper)\n\n## Contributing\n\nWe welcome all contributors, from casual to regular 💙\n\n- **Bug report**. Is something not working as expected? [Send a bug report][contributing-bugreport].\n- **Feature request**. Would you like to add something to the library? [Send a feature request][contributing-featurerequest].\n- **Development**. If you don't know where to start, you can check the open issues that are [tagged easy][contributing-label-easy], the [bugs][contributing-label-bug] or [chores][contributing-label-chore].\n\nTo start contributing to the code, you need to:\n\n1.  [Fork the project](https://help.github.com/articles/fork-a-repo/)\n1.  [Clone the repository](https://help.github.com/articles/cloning-a-repository/)\n1.  Install the dependencies: `yarn`\n\nPlease read [our contribution process](https://github.com/algolia/instantsearch/blob/master/CONTRIBUTING.md) to learn more.\n\n## License\n\nCreate InstantSearch App is [MIT licensed](LICENSE).\n\n<!-- Badges -->\n\n[version-svg]: https://img.shields.io/npm/v/create-instantsearch-app.svg?style=flat-square\n[package-url]: https://npmjs.org/package/create-instantsearch-app\n[ci-svg]: https://img.shields.io/circleci/project/github/algolia/create-instantsearch-app.svg?style=flat-square\n[ci-url]: https://circleci.com/gh/algolia/instantsearch\n[license-image]: https://img.shields.io/badge/license-MIT-green.svg?style=flat-square\n[license-url]: LICENSE\n\n<!-- Links -->\n\n[algolia-website]: https://www.algolia.com/?utm_medium=social-owned&utm_source=GitHub&utm_campaign=create-instantsearch-app%20repository\n[instantsearchjs-github]: https://github.com/algolia/instantsearch/tree/master/packages/instantsearch.js\n[react-instantsearch-github]: https://github.com/algolia/instantsearch/tree/master/packages/react-instantsearch\n[vue-instantsearch-github]: https://github.com/algolia/instantsearch/tree/master/packages/vue-instantsearch\n[instantsearch-ios-github]: https://github.com/algolia/instantsearch-ios\n[instantsearch-android-github]: https://github.com/algolia/instantsearch-android\n[contributing-bugreport]: https://github.com/algolia/instantsearch/issues/new?template=BUG_REPORT.yml&labels=triage,Library%3A%20Create+InstantSearch+App\n[contributing-featurerequest]: https://github.com/algolia/instantsearch/discussions/new?category=ideas&labels=triage,Library%3A%20Create+InstantSearch+App&title=Feature%20request%3A%20\n[contributing-label-easy]: https://github.com/algolia/instantsearch/issues?q=is%3Aopen+is%3Aissue+label%3A%22Difficulty%3A+Easy%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n[contributing-label-bug]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Bug%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n[contributing-label-chore]: https://github.com/algolia/instantsearch/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Chore%22+label%3A%22Library%3A%20Create+InstantSearch+App%22\n\n<!-- File: packages/create-instantsearch-app/src/templates/Autocomplete/README.md (Tokens: ~121 | Category: plugin-manifest) -->\n# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n\n<!-- File: packages/create-instantsearch-app/src/templates/InstantSearch.js widget/README.md (Tokens: ~895 | Category: plugin-manifest) -->\n# {{ name }}\n\n{{ description }}\n\n---\n\n[![MIT](https://img.shields.io/npm/l/{{ packageName }})](./LICENSE) [![NPM version](http://img.shields.io/npm/v/{{ packageName }}.svg)](https://npmjs.org/package/{{ packageName }})\n\n## Install\n\n```bash\nnpm install {{ packageName }}\n# or\nyarn add {{ packageName }}\n```\n\n## Widget\n\n### Usage\n\n```js\nimport instantsearch from 'instantsearch.js';\nimport algoliasearch from 'algoliasearch/lite';\nimport { {{ camelCaseName }} } from '{{ packageName }}';\n\nconst searchClient = algoliasearch('appId', 'apiKey');\n\nconst search = instantsearch({\n  indexName: 'indexName',\n  searchClient,\n});\n\nsearch.addWidgets([\n  {{ camelCaseName }}({\n    // widget parameters\n  }),\n]);\n\nsearch.start();\n```\n\n### Options\n\n| Option | Type | Required | Default | Description |\n| :-- | :-- | :-- | :-- | --- |\n| [`container`](#container) | `string` or `HTMLElement` | true | - | The element to insert the widget into. |\n| [`option1`](#option1) | `...` | true | - | REPLACE WITH THE DESCRIPTION FOR THIS OPTION |\n\n#### container\n\n> `string | Element` | **required**\n\nThe element to insert the widget into.\n\nThis can be either a valid CSS Selector:\n\n```js\n{{ camelCaseName }}({\n  container: '#{{ name }}',\n  // ...\n});\n```\n\nor an `HTMLElement`:\n\n```js\n{{ camelCaseName }}({\n  container: document.querySelector('#{{ name }}'),\n  // ...\n});\n```\n\n#### option1\n\n> `...` | **required**\n\nREPLACE WITH THE DESCRIPTION FOR THIS OPTION\n\n```js\n{{ camelCaseName }}({\n  option1: 'value',\n  // ...\n});\n```\n\n## Connector\n\n### Usage\n\n```js\nimport { connect{{ pascalCaseName }} } from '{{ packageName }}';\n\n// 1. Create a render function\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  // Rendering logic\n};\n\n// 2. Create the custom widget\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }}\n);\n\n// 3. Instantiate\nsearch.addWidgets([\n  custom{{ pascalCaseName }}({\n    // instance params\n  }),\n]);\n```\n\n### Options\n\n#### option1\n\n> `...`\n\nREPLACE WITH THE DESCRIPTION FOR THIS RENDERING ITEM\n\n```js\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  // show how to use this render option\n};\n\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }},\n);\n\nsearch.addWidgets([\n  custom{{pascalCaseName }}({\n    // ...\n  }),\n]);\n```\n\n#### widgetParams\n\n> `object`\n\nAll original widget options forwarded to the render function.\n\n```js\nconst render{{ pascalCaseName }} = (renderOptions, isFirstRender) => {\n  const { widgetParams } = renderOptions;\n  widgetParams.container.innerHTML = '...';\n};\n\nconst custom{{ pascalCaseName }} = connect{{ pascalCaseName }}(\n  render{{ pascalCaseName }},\n);\n\nsearch.addWidgets([\n  custom{{ pascalCaseName }}({\n    container: document.querySelector('#{{ name }}'),\n    // ...\n  }),\n]);\n```\n\n## Contributing\n\nTo start contributing to code, you need to:\n\n1. [Fork the project](https://docs.github.com/en/get-started/quickstart/fork-a-repo)\n2. [Clone the repository](https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository-from-github/cloning-a-repository)\n3. Install the dependencies: `yarn`\n4. Run the development mode: `yarn start`\n\nPlease read [our contribution process](./CONTRIBUTING.md) to learn more.\n\n---\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n<!-- File: packages/create-instantsearch-app/src/templates/InstantSearch.js/README.md (Tokens: ~121 | Category: plugin-manifest) -->\n# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n\n<!-- File: packages/create-instantsearch-app/src/templates/JavaScript Client/README.md (Tokens: ~121 | Category: plugin-manifest) -->\n# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n\n<!-- File: packages/create-instantsearch-app/src/templates/JavaScript Helper/README.md (Tokens: ~121 | Category: plugin-manifest) -->\n# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\nOpen http://localhost:3000 to see your app.\n\n<!-- File: packages/create-instantsearch-app/src/templates/React InstantSearch Native/README.md (Tokens: ~109 | Category: plugin-manifest) -->\n# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\n<!-- File: packages/create-instantsearch-app/src/templates/React InstantSearch/README.md (Tokens: ~109 | Category: plugin-manifest) -->\n# {{name}}\n\n_This project was generated with [create-instantsearch-app](https://github.com/algolia/instantsearch/tree/master/packages/create-instantsearch-app) by [Algolia](https://algolia.com)._\n\n## Get started\n\nTo run this project locally, install the dependencies and run the local server:\n\n```sh\nnpm install\nnpm start\n```\n\nAlternatively, you may use [Yarn](https://http://yarnpkg.com/):\n\n```sh\nyarn\nyarn start\n```\n\n<!-- ... +13 more modular skills available via /api/skills/algolia/instantsearch?path={path} -->\n</yakaai_skills>"}