{"id":"a332ab54-a113-49e3-8d25-920ef74fb1e4","shortId":"QS8qQk","kind":"skill","title":"algolia-search","tagline":"Expert patterns for Algolia search implementation, indexing","description":"# Algolia Search Integration\n\nExpert patterns for Algolia search implementation, indexing strategies, React InstantSearch, and relevance tuning\n\n## Patterns\n\n### React InstantSearch with Hooks\n\nModern React InstantSearch setup using hooks for type-ahead search.\n\nUses react-instantsearch-hooks-web package with algoliasearch client.\nWidgets are components that can be customized with classnames.\n\nKey hooks:\n- useSearchBox: Search input handling\n- useHits: Access search results\n- useRefinementList: Facet filtering\n- usePagination: Result pagination\n- useInstantSearch: Full state access\n\n### Code_example\n\n// lib/algolia.ts\nimport algoliasearch from 'algoliasearch/lite';\n\nexport const searchClient = algoliasearch(\n  process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,\n  process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!  // Search-only key!\n);\n\nexport const INDEX_NAME = 'products';\n\n// components/Search.tsx\n'use client';\nimport { InstantSearch, SearchBox, Hits, Configure } from 'react-instantsearch';\nimport { searchClient, INDEX_NAME } from '@/lib/algolia';\n\nfunction Hit({ hit }: { hit: ProductHit }) {\n  return (\n    <article>\n      <h3>{hit.name}</h3>\n      <p>{hit.description}</p>\n      <span>${hit.price}</span>\n    </article>\n  );\n}\n\nexport function ProductSearch() {\n  return (\n    <InstantSearch searchClient={searchClient} indexName={INDEX_NAME}>\n      <Configure hitsPerPage={20} />\n      <SearchBox\n        placeholder=\"Search products...\"\n        classNames={{\n          root: 'relative',\n          input: 'w-full px-4 py-2 border rounded',\n        }}\n      />\n      <Hits hitComponent={Hit} />\n    </InstantSearch>\n  );\n}\n\n// Custom hook usage\nimport { useSearchBox, useHits, useInstantSearch } from 'react-instantsearch';\n\nfunction CustomSearch() {\n  const { query, refine } = useSearchBox();\n  const { hits } = useHits<ProductHit>();\n  const { status } = useInstantSearch();\n\n  return (\n    <div>\n      <input\n        value={query}\n        onChange={(e) => refine(e.target.value)}\n        placeholder=\"Search...\"\n      />\n      {status === 'loading' && <p>Loading...</p>}\n      <ul>\n        {hits.map((hit) => (\n          <li key={hit.objectID}>{hit.name}</li>\n        ))}\n      </ul>\n    </div>\n  );\n}\n\n### Anti_patterns\n\n- Pattern: Using Admin API key in frontend code | Why: Admin key exposes full index control including deletion | Fix: Use search-only API key with restrictions\n- Pattern: Not using /lite client for frontend | Why: Full client includes unnecessary code for search | Fix: Import from algoliasearch/lite for smaller bundle\n\n### References\n\n- https://www.algolia.com/doc/api-reference/widgets/react\n- https://www.algolia.com/doc/libraries/javascript/v5/methods/search/\n\n### Next.js Server-Side Rendering\n\nSSR integration for Next.js with react-instantsearch-nextjs package.\n\nUse <InstantSearchNext> instead of <InstantSearch> for SSR.\nSupports both Pages Router and App Router (experimental).\n\nKey considerations:\n- Set dynamic = 'force-dynamic' for fresh results\n- Handle URL synchronization with routing prop\n- Use getServerState for initial state\n\n### Code_example\n\n// app/search/page.tsx\nimport { InstantSearchNext } from 'react-instantsearch-nextjs';\nimport { searchClient, INDEX_NAME } from '@/lib/algolia';\nimport { SearchBox, Hits, RefinementList } from 'react-instantsearch';\n\n// Force dynamic rendering for fresh search results\nexport const dynamic = 'force-dynamic';\n\nexport default function SearchPage() {\n  return (\n    <InstantSearchNext\n      searchClient={searchClient}\n      indexName={INDEX_NAME}\n      routing={{\n        router: {\n          cleanUrlOnDispose: false,\n        },\n      }}\n    >\n      <div className=\"flex gap-8\">\n        <aside className=\"w-64\">\n          <h3>Categories</h3>\n          <RefinementList attribute=\"category\" />\n          <h3>Brand</h3>\n          <RefinementList attribute=\"brand\" />\n        </aside>\n        <main className=\"flex-1\">\n          <SearchBox placeholder=\"Search products...\" />\n          <Hits hitComponent={ProductHit} />\n        </main>\n      </div>\n    </InstantSearchNext>\n  );\n}\n\n// For custom routing (URL synchronization)\nimport { history } from 'instantsearch.js/es/lib/routers';\nimport { simple } from 'instantsearch.js/es/lib/stateMappings';\n\n<InstantSearchNext\n  searchClient={searchClient}\n  indexName={INDEX_NAME}\n  routing={{\n    router: history({\n      getLocation: () =>\n        typeof window === 'undefined'\n          ? new URL(url) as unknown as Location\n          : window.location,\n    }),\n    stateMapping: simple(),\n  }}\n>\n  {/* widgets */}\n</InstantSearchNext>\n\n### Anti_patterns\n\n- Pattern: Using InstantSearch component for Next.js SSR | Why: Regular component doesn't support server-side rendering | Fix: Use InstantSearchNext from react-instantsearch-nextjs\n- Pattern: Static rendering for search pages | Why: Search results must be fresh for each request | Fix: Set export const dynamic = 'force-dynamic'\n\n### References\n\n- https://www.npmjs.com/package/react-instantsearch-nextjs\n- https://www.algolia.com/developers/code-exchange/instantsearch-and-next-js-starter\n\n### Data Synchronization and Indexing\n\nIndexing strategies for keeping Algolia in sync with your data.\n\nThree main approaches:\n1. Full Reindexing - Replace entire index (expensive)\n2. Full Record Updates - Replace individual records\n3. Partial Updates - Update specific attributes only\n\nBest practices:\n- Batch records (ideal: 10MB, 1K-10K records per batch)\n- Use incremental updates when possible\n- partialUpdateObjects for attribute-only changes\n- Avoid deleteBy (computationally expensive)\n\n### Code_example\n\n// lib/algolia-admin.ts (SERVER ONLY)\nimport algoliasearch from 'algoliasearch';\n\n// Admin client - NEVER expose to frontend\nconst adminClient = algoliasearch(\n  process.env.ALGOLIA_APP_ID!,\n  process.env.ALGOLIA_ADMIN_KEY!  // Admin key for indexing\n);\n\nconst index = adminClient.initIndex('products');\n\n// Batch indexing (recommended approach)\nexport async function indexProducts(products: Product[]) {\n  const records = products.map((p) => ({\n    objectID: p.id,  // Required unique identifier\n    name: p.name,\n    description: p.description,\n    price: p.price,\n    category: p.category,\n    inStock: p.inventory > 0,\n    createdAt: p.createdAt.getTime(),  // Use timestamps for sorting\n  }));\n\n  // Batch in chunks of ~1000-5000 records\n  const BATCH_SIZE = 1000;\n  for (let i = 0; i < records.length; i += BATCH_SIZE) {\n    const batch = records.slice(i, i + BATCH_SIZE);\n    await index.saveObjects(batch);\n  }\n}\n\n// Partial update - update only specific fields\nexport async function updateProductPrice(productId: string, price: number) {\n  await index.partialUpdateObject({\n    objectID: productId,\n    price,\n    updatedAt: Date.now(),\n  });\n}\n\n// Partial update with operations\nexport async function incrementViewCount(productId: string) {\n  await index.partialUpdateObject({\n    objectID: productId,\n    viewCount: {\n      _operation: 'Increment',\n      value: 1,\n    },\n  });\n}\n\n// Delete records (prefer this over deleteBy)\nexport async function deleteProducts(productIds: string[]) {\n  await index.deleteObjects(productIds);\n}\n\n// Full reindex with zero-downtime (atomic swap)\nexport async function fullReindex(products: Product[]) {\n  const tempIndex = adminClient.initIndex('products_temp');\n\n  // Index to temp index\n  await tempIndex.saveObjects(\n    products.map((p) => ({\n      objectID: p.id,\n      ...p,\n    }))\n  );\n\n  // Copy settings from main index\n  await adminClient.copyIndex('products', 'products_temp', {\n    scope: ['settings', 'synonyms', 'rules'],\n  });\n\n  // Atomic swap\n  await adminClient.moveIndex('products_temp', 'products');\n}\n\n### Anti_patterns\n\n- Pattern: Using deleteBy for bulk deletions | Why: deleteBy is computationally expensive and rate limited | Fix: Use deleteObjects with array of objectIDs\n- Pattern: Indexing one record at a time | Why: Creates indexing queue, slows down process | Fix: Batch records in groups of 1K-10K\n- Pattern: Full reindex for small changes | Why: Wastes operations, slower than incremental | Fix: Use partialUpdateObject for attribute changes\n\n### References\n\n- https://www.algolia.com/doc/guides/sending-and-managing-data/send-and-update-your-data/in-depth/the-different-synchronization-strategies\n- https://www.algolia.com/blog/engineering/search-indexing-best-practices-for-top-performance-with-code-samples\n\n### API Key Security and Restrictions\n\nSecure API key configuration for Algolia.\n\nKey types:\n- Admin API Key: Full control (indexing, settings, deletion)\n- Search-Only API Key: Safe for frontend\n- Secured API Keys: Generated from base key with restrictions\n\nRestrictions available:\n- Indices: Limit accessible indices\n- Rate limit: Limit API calls per hour per IP\n- Validity: Set expiration time\n- HTTP referrers: Restrict to specific URLs\n- Query parameters: Enforce search parameters\n\n### Code_example\n\n// NEVER do this - admin key in frontend\n// const client = algoliasearch(appId, ADMIN_KEY);  // WRONG!\n\n// Correct: Use search-only key in frontend\nconst searchClient = algoliasearch(\n  process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,\n  process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!\n);\n\n// Server-side: Generate secured API key\n// lib/algolia-secured-key.ts\nimport algoliasearch from 'algoliasearch';\n\nconst adminClient = algoliasearch(\n  process.env.ALGOLIA_APP_ID!,\n  process.env.ALGOLIA_ADMIN_KEY!\n);\n\n// Generate user-specific secured key\nexport function generateSecuredKey(userId: string) {\n  const searchKey = process.env.ALGOLIA_SEARCH_KEY!;\n\n  return adminClient.generateSecuredApiKey(searchKey, {\n    // User can only see their own data\n    filters: `userId:${userId}`,\n    // Key expires in 1 hour\n    validUntil: Math.floor(Date.now() / 1000) + 3600,\n    // Restrict to specific index\n    restrictIndices: ['user_documents'],\n  });\n}\n\n// Rate-limited key for public APIs\nexport async function createRateLimitedKey() {\n  const { key } = await adminClient.addApiKey({\n    acl: ['search'],\n    indexes: ['products'],\n    description: 'Public search with rate limit',\n    maxQueriesPerIPPerHour: 1000,\n    referers: ['https://mysite.com/*'],\n    validity: 0,  // Never expires\n  });\n\n  return key;\n}\n\n// API endpoint to get user's secured key\n// app/api/search-key/route.ts\nimport { auth } from '@/lib/auth';\nimport { generateSecuredKey } from '@/lib/algolia-secured-key';\n\nexport async function GET() {\n  const session = await auth();\n  if (!session?.user) {\n    return Response.json({ error: 'Unauthorized' }, { status: 401 });\n  }\n\n  const securedKey = generateSecuredKey(session.user.id);\n\n  return Response.json({ key: securedKey });\n}\n\n### Anti_patterns\n\n- Pattern: Hardcoding Admin API key in client code | Why: Exposes full index control to attackers | Fix: Use search-only key with restrictions\n- Pattern: Using same key for all users | Why: Can't restrict data access per user | Fix: Generate secured API keys with user filters\n- Pattern: No rate limiting on public search | Why: Bots can exhaust your search quota | Fix: Set maxQueriesPerIPPerHour on API key\n\n### References\n\n- https://www.algolia.com/doc/guides/security/api-keys\n- https://support.algolia.com/hc/en-us/articles/14339249272977-What-are-the-best-practices-to-manage-Algolia-API-keys-in-my-code-and-protect-them\n\n### Custom Ranking and Relevance Tuning\n\nConfigure searchable attributes and custom ranking for relevance.\n\nSearchable attributes (order matters):\n1. Most important fields first (title, name)\n2. Secondary fields next (description, tags)\n3. Exclude non-searchable fields (image_url, id)\n\nCustom ranking:\n- Add business metrics (popularity, rating, date)\n- Use desc() for descending, asc() for ascending\n\n### Code_example\n\n// scripts/configure-index.ts\nimport algoliasearch from 'algoliasearch';\n\nconst adminClient = algoliasearch(\n  process.env.ALGOLIA_APP_ID!,\n  process.env.ALGOLIA_ADMIN_KEY!\n);\n\nconst index = adminClient.initIndex('products');\n\nasync function configureIndex() {\n  await index.setSettings({\n    // Searchable attributes in order of importance\n    searchableAttributes: [\n      'name',              // Most important\n      'brand',\n      'category',\n      'description',       // Least important\n    ],\n\n    // Attributes for faceting/filtering\n    attributesForFaceting: [\n      'category',\n      'brand',\n      'filterOnly(inStock)',  // Filter only, not displayed\n      'searchable(tags)',     // Searchable facet\n    ],\n\n    // Custom ranking (after text relevance)\n    customRanking: [\n      'desc(popularity)',     // Most popular first\n      'desc(rating)',         // Then by rating\n      'desc(createdAt)',      // Then by recency\n    ],\n\n    // Typo tolerance\n    typoTolerance: true,\n    minWordSizefor1Typo: 4,\n    minWordSizefor2Typos: 8,\n\n    // Query settings\n    queryLanguages: ['en'],\n    removeStopWords: ['en'],\n\n    // Highlighting\n    attributesToHighlight: ['name', 'description'],\n    highlightPreTag: '<mark>',\n    highlightPostTag: '</mark>',\n\n    // Pagination\n    hitsPerPage: 20,\n    paginationLimitedTo: 1000,\n\n    // Distinct (deduplication)\n    attributeForDistinct: 'productFamily',\n    distinct: true,\n  });\n\n  // Add synonyms\n  await index.saveSynonyms([\n    {\n      objectID: 'phone-mobile',\n      type: 'synonym',\n      synonyms: ['phone', 'mobile', 'cell', 'smartphone'],\n    },\n    {\n      objectID: 'laptop-notebook',\n      type: 'oneWaySynonym',\n      input: 'laptop',\n      synonyms: ['notebook', 'portable computer'],\n    },\n  ]);\n\n  // Add rules (query-based customization)\n  await index.saveRules([\n    {\n      objectID: 'boost-sale-items',\n      condition: {\n        anchoring: 'contains',\n        pattern: 'sale',\n      },\n      consequence: {\n        params: {\n          filters: 'onSale:true',\n          optionalFilters: ['featured:true'],\n        },\n      },\n    },\n  ]);\n\n  console.log('Index configured successfully');\n}\n\nconfigureIndex();\n\n### Anti_patterns\n\n- Pattern: Searching all attributes equally | Why: Reduces relevance, matches in descriptions rank same as titles | Fix: Order searchableAttributes by importance\n- Pattern: No custom ranking | Why: Relies only on text matching, ignores business value | Fix: Add popularity, rating, or recency to customRanking\n- Pattern: Indexing raw dates as strings | Why: Can't sort by date correctly | Fix: Use timestamps (getTime()) for date sorting\n\n### References\n\n- https://www.algolia.com/doc/guides/managing-results/relevance-overview\n- https://www.algolia.com/doc/guides/managing-results/must-do/custom-ranking\n\n### Faceted Search and Filtering\n\nImplement faceted navigation with refinement lists, range sliders,\nand hierarchical menus.\n\nWidget types:\n- RefinementList: Multi-select checkboxes\n- Menu: Single-select list\n- HierarchicalMenu: Nested categories\n- RangeInput/RangeSlider: Numeric ranges\n- ToggleRefinement: Boolean filters\n\n### Code_example\n\n'use client';\nimport {\n  InstantSearch,\n  SearchBox,\n  Hits,\n  RefinementList,\n  HierarchicalMenu,\n  RangeInput,\n  ToggleRefinement,\n  ClearRefinements,\n  CurrentRefinements,\n  Stats,\n  SortBy,\n} from 'react-instantsearch';\nimport { searchClient, INDEX_NAME } from '@/lib/algolia';\n\nexport function ProductSearch() {\n  return (\n    <InstantSearch searchClient={searchClient} indexName={INDEX_NAME}>\n      <div className=\"flex gap-8\">\n        {/* Filters Sidebar */}\n        <aside className=\"w-64 space-y-6\">\n          <ClearRefinements />\n          <CurrentRefinements />\n\n          {/* Category hierarchy */}\n          <div>\n            <h3 className=\"font-semibold mb-2\">Categories</h3>\n            <HierarchicalMenu\n              attributes={[\n                'categories.lvl0',\n                'categories.lvl1',\n                'categories.lvl2',\n              ]}\n              limit={10}\n              showMore\n            />\n          </div>\n\n          {/* Brand filter */}\n          <div>\n            <h3 className=\"font-semibold mb-2\">Brand</h3>\n            <RefinementList\n              attribute=\"brand\"\n              searchable\n              searchablePlaceholder=\"Search brands...\"\n              showMore\n              limit={5}\n              showMoreLimit={20}\n            />\n          </div>\n\n          {/* Price range */}\n          <div>\n            <h3 className=\"font-semibold mb-2\">Price</h3>\n            <RangeInput\n              attribute=\"price\"\n              precision={0}\n              classNames={{\n                input: 'w-20 px-2 py-1 border rounded',\n              }}\n            />\n          </div>\n\n          {/* In stock toggle */}\n          <ToggleRefinement\n            attribute=\"inStock\"\n            label=\"In Stock Only\"\n            on={true}\n          />\n\n          {/* Rating filter */}\n          <div>\n            <h3 className=\"font-semibold mb-2\">Rating</h3>\n            <RefinementList\n              attribute=\"rating\"\n              transformItems={(items) =>\n                items.map((item) => ({\n                  ...item,\n                  label: '★'.repeat(Number(item.label)),\n                }))\n              }\n            />\n          </div>\n        </aside>\n\n        {/* Results */}\n        <main className=\"flex-1\">\n          <div className=\"flex justify-between items-center mb-4\">\n            <SearchBox placeholder=\"Search products...\" />\n            <SortBy\n              items={[\n                { label: 'Relevance', value: 'products' },\n                { label: 'Price (Low to High)', value: 'products_price_asc' },\n                { label: 'Price (High to Low)', value: 'products_price_desc' },\n                { label: 'Rating', value: 'products_rating_desc' },\n              ]}\n            />\n          </div>\n          <Stats />\n          <Hits hitComponent={ProductHit} />\n        </main>\n      </div>\n    </InstantSearch>\n  );\n}\n\n// For sorting, create replica indices\n// products_price_asc: customRanking: ['asc(price)']\n// products_price_desc: customRanking: ['desc(price)']\n// products_rating_desc: customRanking: ['desc(rating)']\n\n### Anti_patterns\n\n- Pattern: Faceting on non-faceted attributes | Why: Must declare attributesForFaceting in settings | Fix: Add attributes to attributesForFaceting array\n- Pattern: Not using filterOnly() for hidden filters | Why: Wastes facet computation on non-displayed attributes | Fix: Use filterOnly(attribute) for filters you won't show\n\n### References\n\n- https://www.algolia.com/doc/guides/managing-results/refine-results/faceting\n- https://www.algolia.com/doc/api-reference/widgets/refinement-list/react\n\n### Query Suggestions and Autocomplete\n\nImplement autocomplete with query suggestions and instant results.\n\nUses @algolia/autocomplete-js for standalone autocomplete or\nintegrate with InstantSearch using SearchBox.\n\nQuery Suggestions require a separate index generated by Algolia.\n\n### Code_example\n\n// Standalone Autocomplete\n// components/Autocomplete.tsx\n'use client';\nimport { autocomplete, getAlgoliaResults } from '@algolia/autocomplete-js';\nimport algoliasearch from 'algoliasearch/lite';\nimport { useEffect, useRef } from 'react';\nimport '@algolia/autocomplete-theme-classic';\n\nconst searchClient = algoliasearch(\n  process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,\n  process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!\n);\n\nexport function Autocomplete() {\n  const containerRef = useRef<HTMLDivElement>(null);\n\n  useEffect(() => {\n    if (!containerRef.current) return;\n\n    const search = autocomplete({\n      container: containerRef.current,\n      placeholder: 'Search for products',\n      openOnFocus: true,\n      getSources({ query }) {\n        if (!query) return [];\n\n        return [\n          // Query suggestions\n          {\n            sourceId: 'suggestions',\n            getItems() {\n              return getAlgoliaResults({\n                searchClient,\n                queries: [\n                  {\n                    indexName: 'products_query_suggestions',\n                    query,\n                    params: { hitsPerPage: 5 },\n                  },\n                ],\n              });\n            },\n            templates: {\n              header() {\n                return 'Suggestions';\n              },\n              item({ item, html }) {\n                return html`<span>${item.query}</span>`;\n              },\n            },\n          },\n          // Instant results\n          {\n            sourceId: 'products',\n            getItems() {\n              return getAlgoliaResults({\n                searchClient,\n                queries: [\n                  {\n                    indexName: 'products',\n                    query,\n                    params: { hitsPerPage: 8 },\n                  },\n                ],\n              });\n            },\n            templates: {\n              header() {\n                return 'Products';\n              },\n              item({ item, html }) {\n                return html`\n                  <a href=\"/products/${item.objectID}\">\n                    <img src=\"${item.image}\" alt=\"${item.name}\" />\n                    <span>${item.name}</span>\n                    <span>$${item.price}</span>\n                  </a>\n                `;\n              },\n            },\n            onSelect({ item, setQuery, refresh }) {\n              // Navigate on selection\n              window.location.href = `/products/${item.objectID}`;\n            },\n          },\n        ];\n      },\n    });\n\n    return () => search.destroy();\n  }, []);\n\n  return <div ref={containerRef} />;\n}\n\n// Combined with InstantSearch\nimport { connectSearchBox } from 'react-instantsearch';\nimport { autocomplete } from '@algolia/autocomplete-js';\n\n// Or use built-in Autocomplete widget\nimport { Autocomplete as AlgoliaAutocomplete } from 'react-instantsearch';\n\nexport function SearchWithAutocomplete() {\n  return (\n    <InstantSearch searchClient={searchClient} indexName=\"products\">\n      <AlgoliaAutocomplete\n        placeholder=\"Search products...\"\n        detachedMediaQuery=\"(max-width: 768px)\"\n      />\n      <Hits hitComponent={ProductHit} />\n    </InstantSearch>\n  );\n}\n\n### Anti_patterns\n\n- Pattern: Creating autocomplete without debouncing | Why: Every keystroke triggers search, wastes operations | Fix: Algolia autocomplete handles debouncing automatically\n- Pattern: Not using Query Suggestions index | Why: Missing search analytics for popular queries | Fix: Enable Query Suggestions in Algolia dashboard\n\n### References\n\n- https://www.algolia.com/doc/ui-libraries/autocomplete/introduction/what-is-autocomplete\n- https://www.algolia.com/doc/guides/building-search-ui/ui-and-ux-patterns/query-suggestions/how-to/optimizing-query-suggestions-relevance/js\n\n## Sharp Edges\n\n### Admin API Key in Frontend Code\n\nSeverity: CRITICAL\n\n### Indexing Rate Limits and Throttling\n\nSeverity: HIGH\n\n### Record Size and Index Limits\n\nSeverity: MEDIUM\n\n### PII in Index Names Visible in Network\n\nSeverity: MEDIUM\n\n### Searchable Attributes Order Affects Relevance\n\nSeverity: MEDIUM\n\n### Full Reindex Consumes All Operations\n\nSeverity: MEDIUM\n\n### Every Keystroke Counts as Search Operation\n\nSeverity: MEDIUM\n\n### SSR Hydration Mismatch with InstantSearch\n\nSeverity: MEDIUM\n\n### Replica Indices for Sorting Multiply Storage\n\nSeverity: LOW\n\n### Faceting Requires attributesForFaceting Declaration\n\nSeverity: MEDIUM\n\n## Validation Checks\n\n### Admin API Key in Client Code\n\nSeverity: ERROR\n\nAdmin API key must never be exposed to client-side code\n\nMessage: Admin API key exposed to client. Use search-only key.\n\n### Hardcoded Algolia API Key\n\nSeverity: ERROR\n\nAPI keys should use environment variables\n\nMessage: Hardcoded Algolia credentials. Use environment variables.\n\n### Search Key Used for Indexing\n\nSeverity: ERROR\n\nIndexing operations require admin key, not search key\n\nMessage: Search key used for indexing. Use admin key for write operations.\n\n### Single Record Indexing in Loop\n\nSeverity: WARNING\n\nBatch records together for efficient indexing\n\nMessage: Single record indexing in loop. Use saveObjects for batch indexing.\n\n### Using deleteBy for Deletion\n\nSeverity: WARNING\n\ndeleteBy is expensive and rate-limited\n\nMessage: deleteBy is expensive. Prefer deleteObjects with specific IDs.\n\n### Frequent Full Reindex\n\nSeverity: WARNING\n\nFull reindex wastes operations on unchanged data\n\nMessage: Frequent full reindex. Consider incremental sync for unchanged data.\n\n### Full Client Instead of Lite\n\nSeverity: INFO\n\nUse lite client for smaller bundle in frontend\n\nMessage: Full Algolia client imported. Use algoliasearch/lite for frontend.\n\n### Regular InstantSearch in Next.js\n\nSeverity: WARNING\n\nUse react-instantsearch-nextjs for SSR support\n\nMessage: Using regular InstantSearch. Use InstantSearchNext for Next.js SSR.\n\n### Missing Searchable Attributes Configuration\n\nSeverity: WARNING\n\nConfigure searchableAttributes for better relevance\n\nMessage: No searchableAttributes configured. Set attribute priority for relevance.\n\n### Missing Custom Ranking\n\nSeverity: INFO\n\nCustom ranking improves business relevance\n\nMessage: No customRanking configured. Add business metrics (popularity, rating).\n\n## Collaboration\n\n### Delegation Triggers\n\n- user needs e-commerce checkout -> stripe-integration (Product search leading to purchase)\n- user needs search analytics -> segment-cdp (Track search queries and results)\n- user needs user authentication -> clerk-auth (Secured API keys per user)\n- user needs database setup -> postgres-wizard (Source data for indexing)\n- user needs serverless deployment -> aws-serverless (Lambda for indexing jobs)\n\n## When to Use\n- User mentions or implies: adding search to\n- User mentions or implies: algolia\n- User mentions or implies: instantsearch\n- User mentions or implies: search api\n- User mentions or implies: search functionality\n- User mentions or implies: typeahead\n- User mentions or implies: autocomplete search\n- User mentions or implies: faceted search\n- User mentions or implies: search index\n- User mentions or implies: search as you type\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.","tags":["algolia","search","antigravity","awesome","skills","sickn33","agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows"],"capabilities":["skill","source-sickn33","skill-algolia-search","topic-agent-skills","topic-agentic-skills","topic-ai-agent-skills","topic-ai-agents","topic-ai-coding","topic-ai-workflows","topic-antigravity","topic-antigravity-skills","topic-claude-code","topic-claude-code-skills","topic-codex-cli","topic-codex-skills"],"categories":["antigravity-awesome-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/algolia-search","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add sickn33/antigravity-awesome-skills","source_repo":"https://github.com/sickn33/antigravity-awesome-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 34964 github stars · SKILL.md body (26,204 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-25T00:50:25.232Z","embedding":null,"createdAt":"2026-04-18T21:30:49.637Z","updatedAt":"2026-04-25T00:50:25.232Z","lastSeenAt":"2026-04-25T00:50:25.232Z","tsv":"'-1':1546 '-2':166,1544 '-20':1542 '-4':164 '-5000':613 '/*''],':1017 '/blog/engineering/search-indexing-best-practices-for-top-performance-with-code-samples':814 '/developers/code-exchange/instantsearch-and-next-js-starter':474 '/doc/api-reference/widgets/react':267 '/doc/api-reference/widgets/refinement-list/react':1686 '/doc/guides/building-search-ui/ui-and-ux-patterns/query-suggestions/how-to/optimizing-query-suggestions-relevance/js':1938 '/doc/guides/managing-results/must-do/custom-ranking':1430 '/doc/guides/managing-results/refine-results/faceting':1683 '/doc/guides/managing-results/relevance-overview':1427 '/doc/guides/security/api-keys':1137 '/doc/guides/sending-and-managing-data/send-and-update-your-data/in-depth/the-different-synchronization-strategies':811 '/doc/libraries/javascript/v5/methods/search/':270 '/doc/ui-libraries/autocomplete/introduction/what-is-autocomplete':1935 '/es/lib/routers'';':387 '/es/lib/statemappings'';':393 '/hc/en-us/articles/14339249272977-what-are-the-best-practices-to-manage-algolia-api-keys-in-my-code-and-protect-them':1140 '/lib/algolia':129,335,1492 '/lib/algolia-secured-key':1040 '/lib/auth':1036 '/lite':245 '/package/react-instantsearch-nextjs':471 '/products':1844 '0':601,622,1019,1538 '1':492,677,973,1158 '10':1514 '1000':612,618,978,1013,1296 '10k':521,789 '10mb':518 '1k':520,788 '1k-10k':519,787 '2':499,1165 '20':151,1294,1530 '3':506,1171 '3600':979 '4':1277 '401':1057 '5':1528,1799 '8':1279,1824 'access':69,81,857,1103 'acl':1002 'ad':2319 'add':1182,1303,1330,1397,1649,2244 'admin':218,225,549,562,564,828,888,896,939,1070,1209,1941,2017,2025,2038,2078,2090 'admincli':556,933,1203 'adminclient.addapikey':1001 'adminclient.copyindex':729 'adminclient.generatesecuredapikey':958 'adminclient.initindex':570,709,1213 'adminclient.moveindex':740 'affect':1975 'ahead':41 'algolia':2,7,11,17,95,100,483,825,912,917,1718,1747,1752,1907,1930,2050,2063,2180,2326 'algolia-search':1 'algolia/autocomplete-js':1700,1730,1864 'algolia/autocomplete-theme-classic':1741 'algoliaautocomplet':1875 'algoliasearch':51,86,92,546,548,557,894,909,929,931,934,1199,1201,1204,1732,1744 'algoliasearch/lite':88,260,1734,2184 'analyt':1921,2269 'anchor':1344 'anti':214,418,744,1066,1361,1633,1892 'api':219,238,815,821,829,839,845,862,925,993,1024,1071,1109,1132,1942,2018,2026,2039,2051,2055,2286,2337 'app':96,296,559,913,936,1206,1748 'app/api/search-key/route.ts':1032 'app/search/page.tsx':322 'appid':895 'approach':491,575 'array':764,1653 'asc':1192,1591,1617,1619 'ascend':1194 'ask':2408 'async':577,645,664,685,702,995,1042,1215 'atom':699,737 'attack':1082 'attribut':511,533,806,1148,1155,1221,1235,1366,1509,1520,1535,1553,1565,1641,1650,1669,1673,1973,2212,2226 'attribute-on':532 'attributefordistinct':1299 'attributesforfacet':1238,1645,1652,2011 'attributestohighlight':1287 'auth':1034,1048,2284 'authent':2281 'autocomplet':1690,1692,1703,1722,1727,1757,1768,1862,1870,1873,1896,1908,2353 'automat':1911 'avail':854 'avoid':536 'aw':2306 'await':635,652,669,690,716,728,739,1000,1047,1218,1305,1336 'aws-serverless':2305 'base':849,1334 'batch':515,524,572,608,616,626,629,633,637,782,2102,2117 'best':513 'better':2219 'boolean':1465 'boost':1340 'boost-sale-item':1339 'border':167,1547 'bot':1122 'boundari':2416 'brand':373,1230,1240,1516,1518,1521,1525 'built':1868 'built-in':1867 'bulk':750 'bundl':263,2175 'busi':1183,1394,2238,2245 'call':863 'categori':372,597,1231,1239,1460,1505,1507 'categories.lvl0':1510 'categories.lvl1':1511 'categories.lvl2':1512 'cdp':2272 'cell':1316 'chang':535,795,807 'check':2016 'checkbox':1452 'checkout':2257 'chunk':610 'clarif':2410 'classnam':61,156,1539 'cleanurlondispos':370 'clear':2383 'clearrefin':1479 'clerk':2283 'clerk-auth':2282 'client':52,114,246,251,550,893,1074,1470,1725,2021,2034,2043,2164,2172,2181 'client-sid':2033 'code':82,223,254,320,540,883,1075,1195,1467,1719,1946,2022,2036 'collabor':2249 'combin':1852 'commerc':2256 'compon':55,423,429 'components/autocomplete.tsx':1723 'components/search.tsx':112 'comput':538,755,1329,1664 'condit':1343 'configur':119,149,823,1146,1358,2213,2216,2224,2243 'configureindex':1217,1360 'connectsearchbox':1856 'consequ':1348 'consid':2157 'consider':300 'console.log':1356 'const':90,108,185,189,192,352,463,555,568,582,615,628,707,892,907,932,952,998,1045,1058,1202,1211,1742,1758,1766 'consum':1981 'contain':1345,1769 'containerref':1759,1851 'containerref.current':1764,1770 'control':230,832,1080 'copi':723 'correct':899,1416 'count':1988 'creat':775,1612,1895 'createdat':602,1268 'createratelimitedkey':997 'credenti':2064 'criteria':2419 'critic':1948 'currentrefin':1480 'custom':59,172,378,1141,1150,1180,1251,1335,1385,2231,2235 'customrank':1256,1403,1618,1624,1630,2242 'customsearch':184 'dashboard':1931 'data':475,488,966,1102,2152,2162,2298 'databas':2292 'date':1187,1407,1415,1422 'date.now':658,977 'debounc':1898,1910 'declar':1644,2012 'dedupl':1298 'default':358 'deleg':2250 'delet':232,678,751,835,2122 'deletebi':537,683,748,753,2120,2125,2133 'deleteobject':762,2137 'deleteproduct':687 'deploy':2304 'desc':1189,1257,1262,1267,1600,1606,1623,1625,1629,1631 'descend':1191 'describ':2387 'descript':593,1006,1169,1232,1289,1373 'display':1246,1668 'distinct':1297,1301 'div':1849 'document':986 'doesn':430 'downtim':698 'dynam':302,305,345,353,356,464,467 'e':200,2255 'e-commerc':2254 'e.target.value':202 'edg':1940 'effici':2106 'en':1283,1285 'enabl':1926 'endpoint':1025 'enforc':880 'entir':496 'environ':2059,2066,2399 'environment-specif':2398 'equal':1367 'error':1054,2024,2054,2074 'everi':1900,1986 'exampl':83,321,541,884,1196,1468,1720 'exclud':1172 'exhaust':1124 'expens':498,539,756,2127,2135 'experiment':298 'expert':4,14,2404 'expir':870,971,1021 'export':89,107,139,351,357,462,576,644,663,684,701,947,994,1041,1493,1755,1880 'expos':227,552,1077,2031,2041 'facet':73,1250,1431,1436,1636,1640,1663,2009,2359 'faceting/filtering':1237 'fals':371 'featur':1354 'field':643,1161,1167,1176 'filter':74,967,1113,1243,1350,1434,1466,1503,1517,1562,1660,1675 'filteron':1241,1657,1672 'first':1162,1261 'fix':233,257,437,460,760,781,802,1083,1106,1128,1378,1396,1417,1648,1670,1906,1925 'forc':304,344,355,466 'force-dynam':303,354,465 'frequent':2141,2154 'fresh':307,348,456 'frontend':222,248,554,843,891,906,1945,2177,2186 'full':79,162,228,250,493,500,693,791,831,1078,1979,2142,2146,2155,2163,2179 'fullreindex':704 'function':130,140,183,359,578,646,665,686,703,948,996,1043,1216,1494,1756,1881,2343 'generat':847,923,941,1107,1716 'generatesecuredkey':949,1038,1060 'get':1027,1044 'getalgoliaresult':1728,1789,1816 'getitem':1787,1814 'getloc':403 'getserverst':316 'getsourc':1777 'gettim':1420 'group':785 'handl':67,309,1909 'hardcod':1069,2049,2062 'header':1801,1826 'hidden':1659 'hierarch':1444 'hierarchi':1506 'hierarchicalmenu':1458,1476,1508 'high':1587,1594,1955 'highlight':1286 'highlightposttag':1291 'highlightpretag':1290 'histori':383,402 'hit':118,131,132,133,169,171,190,209,338,374,1474,1607,1889 'hit.description':137 'hit.name':136,213 'hit.objectid':212 'hit.price':138 'hitcompon':170,375,1608,1890 'hits.map':208 'hitsperpag':150,1293,1798,1823 'hook':31,37,47,63,173 'hour':865,974 'html':1806,1808,1831,1833 'http':872 'hydrat':1995 'id':97,560,914,937,1179,1207,1749,2140 'ideal':517 'identifi':590 'ignor':1393 'imag':1177 'implement':9,19,1435,1691 'impli':2318,2325,2330,2335,2341,2347,2352,2358,2364,2370 'import':85,115,124,175,258,323,330,336,382,388,545,928,1033,1037,1160,1198,1225,1229,1234,1382,1471,1487,1726,1731,1735,1740,1855,1861,1872,2182 'improv':2237 'includ':231,252 'increment':526,675,801,2158 'incrementviewcount':666 'index':10,20,109,126,147,229,332,366,398,478,479,497,567,569,573,712,715,727,768,776,833,983,1004,1079,1212,1357,1405,1489,1501,1715,1917,1949,1959,1965,2072,2075,2088,2097,2107,2111,2118,2300,2310,2366 'index.deleteobjects':691 'index.partialupdateobject':653,670 'index.saveobjects':636 'index.saverules':1337 'index.savesynonyms':1306 'index.setsettings':1219 'indexnam':146,365,397,1500,1792,1819,1887 'indexproduct':579 'indic':855,858,1614,2002 'individu':504 'info':2169,2234 'initi':318 'input':66,159,196,1324,1540,2413 'instant':1697,1810 'instantsearch':23,29,34,46,116,123,143,182,283,328,343,422,443,1472,1486,1497,1707,1854,1860,1879,1884,1998,2188,2196,2204,2331 'instantsearch.js':386,392 'instantsearch.js/es/lib/routers'';':385 'instantsearch.js/es/lib/statemappings'';':391 'instantsearchnext':324,362,394,439,2206 'instead':287,2165 'instock':599,1242,1554 'integr':13,277,1705,2260 'ip':867 'item':1342,1568,1570,1571,1578,1804,1805,1829,1830,1837 'item.label':1575 'item.name':1834 'item.objectid':1845 'item.price':1835 'item.query':1809 'items.map':1569 'job':2311 'keep':482 'key':62,102,106,211,220,226,239,299,563,565,816,822,826,830,840,846,850,889,897,904,919,926,940,946,956,970,990,999,1023,1031,1064,1072,1088,1094,1110,1133,1210,1754,1943,2019,2027,2040,2048,2052,2056,2069,2079,2082,2085,2091,2287 'keystrok':1901,1987 'label':1555,1572,1579,1583,1592,1601 'lambda':2308 'laptop':1320,1325 'laptop-notebook':1319 'lead':2263 'least':1233 'let':620 'li':210 'lib/algolia-admin.ts':542 'lib/algolia-secured-key.ts':927 'lib/algolia.ts':84 'limit':759,856,860,861,989,1011,1117,1513,1527,1951,1960,2131,2375 'list':1440,1457 'lite':2167,2171 'load':206,207 'locat':413 'loop':2099,2113 'low':1585,1596,2008 'main':490,726 'match':1371,1392,2384 'math.floor':976 'matter':1157 'maxqueriesperipperhour':1012,1130 'medium':1962,1971,1978,1985,1993,2000,2014 'mention':2316,2323,2328,2333,2339,2345,2350,2356,2362,2368 'menu':1453 'menus':1445 'messag':2037,2061,2083,2108,2132,2153,2178,2201,2221,2240 'metric':1184,2246 'minwordsizefor1typo':1276 'minwordsizefor2typos':1278 'mismatch':1996 'miss':1919,2210,2230,2421 'mobil':1310,1315 'modern':32 'multi':1450 'multi-select':1449 'multipli':2005 'must':454,1643,2028 'mysite.com':1016 'mysite.com/*''],':1015 'name':110,127,148,333,367,399,591,1164,1227,1288,1490,1502,1966 'navig':1437,1840 'need':2253,2267,2279,2291,2302 'nest':1459 'network':1969 'never':551,885,1020,2029 'new':407 'next':1168 'next.js':271,279,425,2190,2208 'nextj':284,329,444,2197 'non':1174,1639,1667 'non-display':1666 'non-facet':1638 'non-search':1173 'notebook':1321,1327 'null':1761 'number':651,1574 'numer':1462 'objectid':586,654,671,720,766,1307,1318,1338 'onchang':199 'one':769 'onewaysynonym':1323 'onsal':1351 'onselect':1836 'openonfocus':1775 'oper':662,674,798,1905,1983,1991,2076,2094,2149 'optionalfilt':1353 'order':1156,1223,1379,1974 'output':2393 'p':585,719,722 'p.category':598 'p.createdat.gettime':603 'p.description':594 'p.id':587,721 'p.inventory':600 'p.name':592 'p.price':596 'packag':49,285 'page':293,450 'pagin':77,1292 'paginationlimitedto':1295 'param':1349,1797,1822 'paramet':879,882 'partial':507,638,659 'partialupdateobject':530,804 'pattern':5,15,27,215,216,242,419,420,445,745,746,767,790,1067,1068,1091,1114,1346,1362,1363,1383,1404,1634,1635,1654,1893,1894,1912 'per':523,864,866,1104,2288 'permiss':2414 'phone':1309,1314 'phone-mobil':1308 'pii':1963 'placehold':153,203,1771 'popular':1185,1258,1260,1398,1923,2247 'portabl':1328 'possibl':529 'postgr':2295 'postgres-wizard':2294 'practic':514 'precis':1537 'prefer':680,2136 'price':595,650,656,1531,1533,1536,1584,1590,1593,1599,1616,1620,1622,1626 'prioriti':2227 'process':780 'process.env.algolia':558,561,935,938,954,1205,1208 'process.env.next':93,98,910,915,1745,1750 'product':111,155,571,580,581,705,706,710,730,731,741,743,1005,1214,1582,1589,1598,1604,1615,1621,1627,1774,1793,1813,1820,1828,1888,2261 'productfamili':1300 'producthit':134,376,1609,1891 'productid':648,655,667,672,688,692 'products.map':584,718 'productsearch':141,1495 'prop':314 'public':94,99,911,916,992,1007,1119,1746,1751 'purchas':2265 'px':163,1543 'py':165,1545 'queri':186,198,878,1280,1333,1687,1694,1710,1778,1780,1783,1791,1794,1796,1818,1821,1915,1924,1927,2275 'query-bas':1332 'querylanguag':1282 'queue':777 'quota':1127 'rang':1441,1463,1532 'rangeinput':1477,1534 'rangeinput/rangeslider':1461 'rank':1142,1151,1181,1252,1374,1386,2232,2236 'rate':758,859,988,1010,1116,1186,1263,1266,1399,1561,1563,1566,1602,1605,1628,1632,1950,2130,2248 'rate-limit':987,2129 'raw':1406 'react':22,28,33,45,122,181,282,327,342,442,1485,1739,1859,1878,2195 'react-instantsearch':121,180,341,1484,1858,1877 'react-instantsearch-hooks-web':44 'react-instantsearch-nextj':281,326,441,2194 'recenc':1271,1401 'recommend':574 'record':501,505,516,522,583,614,679,770,783,1956,2096,2103,2110 'records.length':624 'records.slice':630 'reduc':1369 'ref':1850 'refer':264,468,808,1014,1134,1424,1680,1932 'referr':873 'refin':187,201,1439 'refinementlist':339,1448,1475,1519,1564 'refresh':1839 'regular':428,2187,2203 'reindex':494,694,792,1980,2143,2147,2156 'relat':158 'relev':25,1144,1153,1255,1370,1580,1976,2220,2229,2239 'reli':1388 'removestopword':1284 'render':275,346,436,447 'repeat':1573 'replac':495,503 'replica':1613,2001 'request':459 'requir':588,1712,2010,2077,2412 'response.json':1053,1063 'restrict':241,819,852,853,874,980,1090,1101 'restrictindic':984 'result':71,76,308,350,453,1576,1698,1811,2277 'return':135,142,195,361,957,1022,1052,1062,1496,1765,1781,1782,1788,1802,1807,1815,1827,1832,1846,1848,1883 'review':2405 'root':157 'round':168,1548 'rout':313,368,379,400 'router':294,297,369,401 'rule':736,1331 'safe':841 'safeti':2415 'sale':1341,1347 'saveobject':2115 'scope':733,2386 'scripts/configure-index.ts':1197 'search':3,8,12,18,42,65,70,101,104,154,204,236,256,349,449,452,837,881,902,918,955,1003,1008,1086,1120,1126,1364,1432,1524,1753,1767,1772,1903,1920,1990,2046,2068,2081,2084,2262,2268,2274,2320,2336,2342,2354,2360,2365,2371 'search-on':103,235,836,901,1085,2045 'search.destroy':1847 'searchabl':1147,1154,1175,1220,1247,1249,1522,1972,2211 'searchableattribut':1226,1380,2217,2223 'searchableplacehold':1523 'searchbox':117,152,337,1473,1709 'searchclient':91,125,144,145,331,363,364,395,396,908,1488,1498,1499,1743,1790,1817,1885,1886 'searchkey':953,959 'searchpag':360 'searchwithautocomplet':1882 'secondari':1166 'secur':817,820,844,924,945,1030,1108,2285 'securedkey':1059,1065 'see':963 'segment':2271 'segment-cdp':2270 'select':1451,1456,1842 'separ':1714 'server':273,434,543,921 'server-sid':272,433,920 'serverless':2303,2307 'session':1046,1050 'session.user.id':1061 'set':301,461,724,734,834,869,1129,1281,1647,2225 'setqueri':1838 'setup':35,2293 'sever':1947,1954,1961,1970,1977,1984,1992,1999,2007,2013,2023,2053,2073,2100,2123,2144,2168,2191,2214,2233 'sharp':1939 'show':1679 'showmor':1515,1526 'showmorelimit':1529 'side':274,435,922,2035 'sidebar':1504 'simpl':389,416 'singl':1455,2095,2109 'single-select':1454 'size':617,627,634,1957 'skill':2378 'skill-algolia-search' 'slider':1442 'slow':778 'slower':799 'small':794 'smaller':262,2174 'smartphon':1317 'sort':607,1413,1423,1611,2004 'sortbi':1482,1577 'sourc':2297 'source-sickn33' 'sourceid':1785,1812 'specif':510,642,876,944,982,2139,2400 'ssr':276,290,426,1994,2199,2209 'standalon':1702,1721 'stat':1481 'state':80,319 'statemap':415 'static':446 'status':193,205,1056 'stock':1550,1557 'stop':2406 'storag':2006 'strategi':21,480 'string':649,668,689,951,1409 'stripe':2259 'stripe-integr':2258 'substitut':2396 'success':1359,2418 'suggest':1688,1695,1711,1784,1786,1795,1803,1916,1928 'support':291,432,2200 'support.algolia.com':1139 'support.algolia.com/hc/en-us/articles/14339249272977-what-are-the-best-practices-to-manage-algolia-api-keys-in-my-code-and-protect-them':1138 'swap':700,738 'sync':485,2159 'synchron':311,381,476 'synonym':735,1304,1312,1313,1326 'tag':1170,1248 'task':2382 'temp':711,714,732,742 'tempindex':708 'tempindex.saveobjects':717 'templat':1800,1825 'test':2402 'text':1254,1391 'three':489 'throttl':1953 'time':773,871 'timestamp':605,1419 'titl':1163,1377 'togeth':2104 'toggl':1551 'togglerefin':1464,1478,1552 'toler':1273 'topic-agent-skills' 'topic-agentic-skills' 'topic-ai-agent-skills' 'topic-ai-agents' 'topic-ai-coding' 'topic-ai-workflows' 'topic-antigravity' 'topic-antigravity-skills' 'topic-claude-code' 'topic-claude-code-skills' 'topic-codex-cli' 'topic-codex-skills' 'track':2273 'transformitem':1567 'treat':2391 'trigger':1902,2251 'true':1275,1302,1352,1355,1560,1776 'tune':26,1145 'type':40,827,1311,1322,1447,2374 'type-ahead':39 'typeahead':2348 'typeof':404 'typo':1272 'typotoler':1274 'unauthor':1055 'unchang':2151,2161 'undefin':406 'uniqu':589 'unknown':411 'unnecessari':253 'updat':502,508,509,527,639,640,660 'updatedat':657 'updateproductpric':647 'url':310,380,408,409,877,1178 'usag':174 'use':36,43,113,217,234,244,286,315,421,438,525,604,747,761,803,900,1084,1092,1188,1418,1469,1656,1671,1699,1708,1724,1866,1914,2044,2058,2065,2070,2086,2089,2114,2119,2170,2183,2193,2202,2205,2314,2376 'useeffect':1736,1762 'usehit':68,177,191 'useinstantsearch':78,178,194 'usepagin':75 'user':943,960,985,1028,1051,1097,1105,1112,2252,2266,2278,2280,2289,2290,2301,2315,2322,2327,2332,2338,2344,2349,2355,2361,2367 'user-specif':942 'useref':1737,1760 'userefinementlist':72 'userid':950,968,969 'usesearchbox':64,176,188 'valid':868,1018,2015,2401 'validuntil':975 'valu':197,676,1395,1581,1588,1597,1603 'variabl':2060,2067 'viewcount':673 'visibl':1967 'w':161,1541 'w-full':160 'warn':2101,2124,2145,2192,2215 'wast':797,1662,1904,2148 'web':48 'widget':53,417,1446,1871 'window':405 'window.location':414 'window.location.href':1843 'without':1897 'wizard':2296 'won':1677 'write':2093 'wrong':898 'www.algolia.com':266,269,473,810,813,1136,1426,1429,1682,1685,1934,1937 'www.algolia.com/blog/engineering/search-indexing-best-practices-for-top-performance-with-code-samples':812 'www.algolia.com/developers/code-exchange/instantsearch-and-next-js-starter':472 'www.algolia.com/doc/api-reference/widgets/react':265 'www.algolia.com/doc/api-reference/widgets/refinement-list/react':1684 'www.algolia.com/doc/guides/building-search-ui/ui-and-ux-patterns/query-suggestions/how-to/optimizing-query-suggestions-relevance/js':1936 'www.algolia.com/doc/guides/managing-results/must-do/custom-ranking':1428 'www.algolia.com/doc/guides/managing-results/refine-results/faceting':1681 'www.algolia.com/doc/guides/managing-results/relevance-overview':1425 'www.algolia.com/doc/guides/security/api-keys':1135 'www.algolia.com/doc/guides/sending-and-managing-data/send-and-update-your-data/in-depth/the-different-synchronization-strategies':809 'www.algolia.com/doc/libraries/javascript/v5/methods/search/':268 'www.algolia.com/doc/ui-libraries/autocomplete/introduction/what-is-autocomplete':1933 'www.npmjs.com':470 'www.npmjs.com/package/react-instantsearch-nextjs':469 'zero':697 'zero-downtim':696","prices":[{"id":"f4deb150-ef04-471a-b086-1bc34b7297ed","listingId":"a332ab54-a113-49e3-8d25-920ef74fb1e4","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"sickn33","category":"antigravity-awesome-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T21:30:49.637Z"}],"sources":[{"listingId":"a332ab54-a113-49e3-8d25-920ef74fb1e4","source":"github","sourceId":"sickn33/antigravity-awesome-skills/algolia-search","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/algolia-search","isPrimary":false,"firstSeenAt":"2026-04-18T21:30:49.637Z","lastSeenAt":"2026-04-25T00:50:25.232Z"}],"details":{"listingId":"a332ab54-a113-49e3-8d25-920ef74fb1e4","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"algolia-search","github":{"repo":"sickn33/antigravity-awesome-skills","stars":34964,"topics":["agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows","antigravity","antigravity-skills","claude-code","claude-code-skills","codex-cli","codex-skills","cursor","cursor-skills","developer-tools","gemini-cli","gemini-skills","kiro","mcp","skill-library"],"license":"mit","html_url":"https://github.com/sickn33/antigravity-awesome-skills","pushed_at":"2026-04-24T06:41:17Z","description":"Installable GitHub library of 1,400+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.","skill_md_sha":"05efcc7f059158fffbd55e45d2b97d2201aa7951","skill_md_path":"skills/algolia-search/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/algolia-search"},"layout":"multi","source":"github","category":"antigravity-awesome-skills","frontmatter":{"name":"algolia-search","description":"Expert patterns for Algolia search implementation, indexing"},"skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/algolia-search"},"updatedAt":"2026-04-25T00:50:25.232Z"}}