opencode-skills-collection 1.0.185 → 1.0.187

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +5 -1
  2. package/bundled-skills/3d-web-experience/SKILL.md +152 -37
  3. package/bundled-skills/agent-evaluation/SKILL.md +1088 -26
  4. package/bundled-skills/agent-memory-systems/SKILL.md +1037 -25
  5. package/bundled-skills/agent-tool-builder/SKILL.md +668 -16
  6. package/bundled-skills/ai-agents-architect/SKILL.md +271 -31
  7. package/bundled-skills/ai-product/SKILL.md +716 -26
  8. package/bundled-skills/ai-wrapper-product/SKILL.md +450 -44
  9. package/bundled-skills/algolia-search/SKILL.md +867 -15
  10. package/bundled-skills/autonomous-agents/SKILL.md +1033 -26
  11. package/bundled-skills/aws-serverless/SKILL.md +1046 -35
  12. package/bundled-skills/azure-functions/SKILL.md +1318 -19
  13. package/bundled-skills/browser-automation/SKILL.md +1065 -28
  14. package/bundled-skills/browser-extension-builder/SKILL.md +159 -32
  15. package/bundled-skills/bullmq-specialist/SKILL.md +347 -16
  16. package/bundled-skills/clerk-auth/SKILL.md +796 -15
  17. package/bundled-skills/computer-use-agents/SKILL.md +1870 -28
  18. package/bundled-skills/context-window-management/SKILL.md +271 -18
  19. package/bundled-skills/conversation-memory/SKILL.md +453 -24
  20. package/bundled-skills/crewai/SKILL.md +252 -46
  21. package/bundled-skills/discord-bot-architect/SKILL.md +1207 -34
  22. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  23. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  24. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  25. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  26. package/bundled-skills/docs/users/bundles.md +1 -1
  27. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  28. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  29. package/bundled-skills/docs/users/getting-started.md +1 -1
  30. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  31. package/bundled-skills/docs/users/usage.md +4 -4
  32. package/bundled-skills/docs/users/visual-guide.md +4 -4
  33. package/bundled-skills/email-systems/SKILL.md +646 -26
  34. package/bundled-skills/faf-expert/SKILL.md +221 -0
  35. package/bundled-skills/faf-wizard/SKILL.md +252 -0
  36. package/bundled-skills/file-uploads/SKILL.md +212 -11
  37. package/bundled-skills/firebase/SKILL.md +646 -16
  38. package/bundled-skills/gcp-cloud-run/SKILL.md +1117 -32
  39. package/bundled-skills/graphql/SKILL.md +1026 -27
  40. package/bundled-skills/hubspot-integration/SKILL.md +804 -19
  41. package/bundled-skills/idea-darwin/SKILL.md +120 -0
  42. package/bundled-skills/inngest/SKILL.md +431 -16
  43. package/bundled-skills/interactive-portfolio/SKILL.md +342 -44
  44. package/bundled-skills/langfuse/SKILL.md +296 -41
  45. package/bundled-skills/langgraph/SKILL.md +259 -50
  46. package/bundled-skills/micro-saas-launcher/SKILL.md +343 -44
  47. package/bundled-skills/neon-postgres/SKILL.md +572 -15
  48. package/bundled-skills/nextjs-supabase-auth/SKILL.md +269 -21
  49. package/bundled-skills/notion-template-business/SKILL.md +371 -44
  50. package/bundled-skills/personal-tool-builder/SKILL.md +537 -44
  51. package/bundled-skills/plaid-fintech/SKILL.md +825 -19
  52. package/bundled-skills/prompt-caching/SKILL.md +438 -25
  53. package/bundled-skills/rag-engineer/SKILL.md +271 -29
  54. package/bundled-skills/salesforce-development/SKILL.md +912 -19
  55. package/bundled-skills/satori/SKILL.md +54 -0
  56. package/bundled-skills/scroll-experience/SKILL.md +381 -44
  57. package/bundled-skills/segment-cdp/SKILL.md +817 -19
  58. package/bundled-skills/shopify-apps/SKILL.md +1475 -19
  59. package/bundled-skills/slack-bot-builder/SKILL.md +1162 -28
  60. package/bundled-skills/telegram-bot-builder/SKILL.md +152 -37
  61. package/bundled-skills/telegram-mini-app/SKILL.md +445 -44
  62. package/bundled-skills/trigger-dev/SKILL.md +916 -27
  63. package/bundled-skills/twilio-communications/SKILL.md +1310 -28
  64. package/bundled-skills/upstash-qstash/SKILL.md +898 -27
  65. package/bundled-skills/vercel-deployment/SKILL.md +637 -39
  66. package/bundled-skills/viral-generator-builder/SKILL.md +132 -37
  67. package/bundled-skills/voice-agents/SKILL.md +937 -27
  68. package/bundled-skills/voice-ai-development/SKILL.md +375 -46
  69. package/bundled-skills/workflow-automation/SKILL.md +982 -29
  70. package/bundled-skills/zapier-make-patterns/SKILL.md +772 -27
  71. package/package.json +1 -1
@@ -1,13 +1,16 @@
1
1
  ---
2
2
  name: algolia-search
3
- description: "Expert patterns for Algolia search implementation, indexing strategies, React InstantSearch, and relevance tuning Use when: adding search to, algolia, instantsearch, search api, search functionality."
3
+ description: Expert patterns for Algolia search implementation, indexing
4
+ strategies, React InstantSearch, and relevance tuning
4
5
  risk: unknown
5
- source: "vibeship-spawner-skills (Apache 2.0)"
6
- date_added: "2026-02-27"
6
+ source: vibeship-spawner-skills (Apache 2.0)
7
+ date_added: 2026-02-27
7
8
  ---
8
9
 
9
10
  # Algolia Search Integration
10
11
 
12
+ Expert patterns for Algolia search implementation, indexing strategies, React InstantSearch, and relevance tuning
13
+
11
14
  ## Patterns
12
15
 
13
16
  ### React InstantSearch with Hooks
@@ -24,6 +27,84 @@ Key hooks:
24
27
  - usePagination: Result pagination
25
28
  - useInstantSearch: Full state access
26
29
 
30
+ ### Code_example
31
+
32
+ // lib/algolia.ts
33
+ import algoliasearch from 'algoliasearch/lite';
34
+
35
+ export const searchClient = algoliasearch(
36
+ process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
37
+ process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY! // Search-only key!
38
+ );
39
+
40
+ export const INDEX_NAME = 'products';
41
+
42
+ // components/Search.tsx
43
+ 'use client';
44
+ import { InstantSearch, SearchBox, Hits, Configure } from 'react-instantsearch';
45
+ import { searchClient, INDEX_NAME } from '@/lib/algolia';
46
+
47
+ function Hit({ hit }: { hit: ProductHit }) {
48
+ return (
49
+ <article>
50
+ <h3>{hit.name}</h3>
51
+ <p>{hit.description}</p>
52
+ <span>${hit.price}</span>
53
+ </article>
54
+ );
55
+ }
56
+
57
+ export function ProductSearch() {
58
+ return (
59
+ <InstantSearch searchClient={searchClient} indexName={INDEX_NAME}>
60
+ <Configure hitsPerPage={20} />
61
+ <SearchBox
62
+ placeholder="Search products..."
63
+ classNames={{
64
+ root: 'relative',
65
+ input: 'w-full px-4 py-2 border rounded',
66
+ }}
67
+ />
68
+ <Hits hitComponent={Hit} />
69
+ </InstantSearch>
70
+ );
71
+ }
72
+
73
+ // Custom hook usage
74
+ import { useSearchBox, useHits, useInstantSearch } from 'react-instantsearch';
75
+
76
+ function CustomSearch() {
77
+ const { query, refine } = useSearchBox();
78
+ const { hits } = useHits<ProductHit>();
79
+ const { status } = useInstantSearch();
80
+
81
+ return (
82
+ <div>
83
+ <input
84
+ value={query}
85
+ onChange={(e) => refine(e.target.value)}
86
+ placeholder="Search..."
87
+ />
88
+ {status === 'loading' && <p>Loading...</p>}
89
+ <ul>
90
+ {hits.map((hit) => (
91
+ <li key={hit.objectID}>{hit.name}</li>
92
+ ))}
93
+ </ul>
94
+ </div>
95
+ );
96
+ }
97
+
98
+ ### Anti_patterns
99
+
100
+ - 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
101
+ - Pattern: Not using /lite client for frontend | Why: Full client includes unnecessary code for search | Fix: Import from algoliasearch/lite for smaller bundle
102
+
103
+ ### References
104
+
105
+ - https://www.algolia.com/doc/api-reference/widgets/react
106
+ - https://www.algolia.com/doc/libraries/javascript/v5/methods/search/
107
+
27
108
  ### Next.js Server-Side Rendering
28
109
 
29
110
  SSR integration for Next.js with react-instantsearch-nextjs package.
@@ -36,6 +117,73 @@ Key considerations:
36
117
  - Handle URL synchronization with routing prop
37
118
  - Use getServerState for initial state
38
119
 
120
+ ### Code_example
121
+
122
+ // app/search/page.tsx
123
+ import { InstantSearchNext } from 'react-instantsearch-nextjs';
124
+ import { searchClient, INDEX_NAME } from '@/lib/algolia';
125
+ import { SearchBox, Hits, RefinementList } from 'react-instantsearch';
126
+
127
+ // Force dynamic rendering for fresh search results
128
+ export const dynamic = 'force-dynamic';
129
+
130
+ export default function SearchPage() {
131
+ return (
132
+ <InstantSearchNext
133
+ searchClient={searchClient}
134
+ indexName={INDEX_NAME}
135
+ routing={{
136
+ router: {
137
+ cleanUrlOnDispose: false,
138
+ },
139
+ }}
140
+ >
141
+ <div className="flex gap-8">
142
+ <aside className="w-64">
143
+ <h3>Categories</h3>
144
+ <RefinementList attribute="category" />
145
+ <h3>Brand</h3>
146
+ <RefinementList attribute="brand" />
147
+ </aside>
148
+ <main className="flex-1">
149
+ <SearchBox placeholder="Search products..." />
150
+ <Hits hitComponent={ProductHit} />
151
+ </main>
152
+ </div>
153
+ </InstantSearchNext>
154
+ );
155
+ }
156
+
157
+ // For custom routing (URL synchronization)
158
+ import { history } from 'instantsearch.js/es/lib/routers';
159
+ import { simple } from 'instantsearch.js/es/lib/stateMappings';
160
+
161
+ <InstantSearchNext
162
+ searchClient={searchClient}
163
+ indexName={INDEX_NAME}
164
+ routing={{
165
+ router: history({
166
+ getLocation: () =>
167
+ typeof window === 'undefined'
168
+ ? new URL(url) as unknown as Location
169
+ : window.location,
170
+ }),
171
+ stateMapping: simple(),
172
+ }}
173
+ >
174
+ {/* widgets */}
175
+ </InstantSearchNext>
176
+
177
+ ### Anti_patterns
178
+
179
+ - Pattern: Using InstantSearch component for Next.js SSR | Why: Regular component doesn't support server-side rendering | Fix: Use InstantSearchNext from react-instantsearch-nextjs
180
+ - Pattern: Static rendering for search pages | Why: Search results must be fresh for each request | Fix: Set export const dynamic = 'force-dynamic'
181
+
182
+ ### References
183
+
184
+ - https://www.npmjs.com/package/react-instantsearch-nextjs
185
+ - https://www.algolia.com/developers/code-exchange/instantsearch-and-next-js-starter
186
+
39
187
  ### Data Synchronization and Indexing
40
188
 
41
189
  Indexing strategies for keeping Algolia in sync with your data.
@@ -51,18 +199,722 @@ Best practices:
51
199
  - partialUpdateObjects for attribute-only changes
52
200
  - Avoid deleteBy (computationally expensive)
53
201
 
54
- ## ⚠️ Sharp Edges
202
+ ### Code_example
203
+
204
+ // lib/algolia-admin.ts (SERVER ONLY)
205
+ import algoliasearch from 'algoliasearch';
206
+
207
+ // Admin client - NEVER expose to frontend
208
+ const adminClient = algoliasearch(
209
+ process.env.ALGOLIA_APP_ID!,
210
+ process.env.ALGOLIA_ADMIN_KEY! // Admin key for indexing
211
+ );
212
+
213
+ const index = adminClient.initIndex('products');
214
+
215
+ // Batch indexing (recommended approach)
216
+ export async function indexProducts(products: Product[]) {
217
+ const records = products.map((p) => ({
218
+ objectID: p.id, // Required unique identifier
219
+ name: p.name,
220
+ description: p.description,
221
+ price: p.price,
222
+ category: p.category,
223
+ inStock: p.inventory > 0,
224
+ createdAt: p.createdAt.getTime(), // Use timestamps for sorting
225
+ }));
226
+
227
+ // Batch in chunks of ~1000-5000 records
228
+ const BATCH_SIZE = 1000;
229
+ for (let i = 0; i < records.length; i += BATCH_SIZE) {
230
+ const batch = records.slice(i, i + BATCH_SIZE);
231
+ await index.saveObjects(batch);
232
+ }
233
+ }
234
+
235
+ // Partial update - update only specific fields
236
+ export async function updateProductPrice(productId: string, price: number) {
237
+ await index.partialUpdateObject({
238
+ objectID: productId,
239
+ price,
240
+ updatedAt: Date.now(),
241
+ });
242
+ }
243
+
244
+ // Partial update with operations
245
+ export async function incrementViewCount(productId: string) {
246
+ await index.partialUpdateObject({
247
+ objectID: productId,
248
+ viewCount: {
249
+ _operation: 'Increment',
250
+ value: 1,
251
+ },
252
+ });
253
+ }
254
+
255
+ // Delete records (prefer this over deleteBy)
256
+ export async function deleteProducts(productIds: string[]) {
257
+ await index.deleteObjects(productIds);
258
+ }
259
+
260
+ // Full reindex with zero-downtime (atomic swap)
261
+ export async function fullReindex(products: Product[]) {
262
+ const tempIndex = adminClient.initIndex('products_temp');
263
+
264
+ // Index to temp index
265
+ await tempIndex.saveObjects(
266
+ products.map((p) => ({
267
+ objectID: p.id,
268
+ ...p,
269
+ }))
270
+ );
271
+
272
+ // Copy settings from main index
273
+ await adminClient.copyIndex('products', 'products_temp', {
274
+ scope: ['settings', 'synonyms', 'rules'],
275
+ });
276
+
277
+ // Atomic swap
278
+ await adminClient.moveIndex('products_temp', 'products');
279
+ }
280
+
281
+ ### Anti_patterns
282
+
283
+ - Pattern: Using deleteBy for bulk deletions | Why: deleteBy is computationally expensive and rate limited | Fix: Use deleteObjects with array of objectIDs
284
+ - Pattern: Indexing one record at a time | Why: Creates indexing queue, slows down process | Fix: Batch records in groups of 1K-10K
285
+ - Pattern: Full reindex for small changes | Why: Wastes operations, slower than incremental | Fix: Use partialUpdateObject for attribute changes
286
+
287
+ ### References
288
+
289
+ - https://www.algolia.com/doc/guides/sending-and-managing-data/send-and-update-your-data/in-depth/the-different-synchronization-strategies
290
+ - https://www.algolia.com/blog/engineering/search-indexing-best-practices-for-top-performance-with-code-samples
291
+
292
+ ### API Key Security and Restrictions
293
+
294
+ Secure API key configuration for Algolia.
295
+
296
+ Key types:
297
+ - Admin API Key: Full control (indexing, settings, deletion)
298
+ - Search-Only API Key: Safe for frontend
299
+ - Secured API Keys: Generated from base key with restrictions
300
+
301
+ Restrictions available:
302
+ - Indices: Limit accessible indices
303
+ - Rate limit: Limit API calls per hour per IP
304
+ - Validity: Set expiration time
305
+ - HTTP referrers: Restrict to specific URLs
306
+ - Query parameters: Enforce search parameters
307
+
308
+ ### Code_example
309
+
310
+ // NEVER do this - admin key in frontend
311
+ // const client = algoliasearch(appId, ADMIN_KEY); // WRONG!
312
+
313
+ // Correct: Use search-only key in frontend
314
+ const searchClient = algoliasearch(
315
+ process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
316
+ process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!
317
+ );
318
+
319
+ // Server-side: Generate secured API key
320
+ // lib/algolia-secured-key.ts
321
+ import algoliasearch from 'algoliasearch';
322
+
323
+ const adminClient = algoliasearch(
324
+ process.env.ALGOLIA_APP_ID!,
325
+ process.env.ALGOLIA_ADMIN_KEY!
326
+ );
327
+
328
+ // Generate user-specific secured key
329
+ export function generateSecuredKey(userId: string) {
330
+ const searchKey = process.env.ALGOLIA_SEARCH_KEY!;
331
+
332
+ return adminClient.generateSecuredApiKey(searchKey, {
333
+ // User can only see their own data
334
+ filters: `userId:${userId}`,
335
+ // Key expires in 1 hour
336
+ validUntil: Math.floor(Date.now() / 1000) + 3600,
337
+ // Restrict to specific index
338
+ restrictIndices: ['user_documents'],
339
+ });
340
+ }
341
+
342
+ // Rate-limited key for public APIs
343
+ export async function createRateLimitedKey() {
344
+ const { key } = await adminClient.addApiKey({
345
+ acl: ['search'],
346
+ indexes: ['products'],
347
+ description: 'Public search with rate limit',
348
+ maxQueriesPerIPPerHour: 1000,
349
+ referers: ['https://mysite.com/*'],
350
+ validity: 0, // Never expires
351
+ });
352
+
353
+ return key;
354
+ }
355
+
356
+ // API endpoint to get user's secured key
357
+ // app/api/search-key/route.ts
358
+ import { auth } from '@/lib/auth';
359
+ import { generateSecuredKey } from '@/lib/algolia-secured-key';
360
+
361
+ export async function GET() {
362
+ const session = await auth();
363
+ if (!session?.user) {
364
+ return Response.json({ error: 'Unauthorized' }, { status: 401 });
365
+ }
366
+
367
+ const securedKey = generateSecuredKey(session.user.id);
368
+
369
+ return Response.json({ key: securedKey });
370
+ }
371
+
372
+ ### Anti_patterns
373
+
374
+ - Pattern: Hardcoding Admin API key in client code | Why: Exposes full index control to attackers | Fix: Use search-only key with restrictions
375
+ - Pattern: Using same key for all users | Why: Can't restrict data access per user | Fix: Generate secured API keys with user filters
376
+ - Pattern: No rate limiting on public search | Why: Bots can exhaust your search quota | Fix: Set maxQueriesPerIPPerHour on API key
377
+
378
+ ### References
379
+
380
+ - https://www.algolia.com/doc/guides/security/api-keys
381
+ - 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
382
+
383
+ ### Custom Ranking and Relevance Tuning
384
+
385
+ Configure searchable attributes and custom ranking for relevance.
386
+
387
+ Searchable attributes (order matters):
388
+ 1. Most important fields first (title, name)
389
+ 2. Secondary fields next (description, tags)
390
+ 3. Exclude non-searchable fields (image_url, id)
391
+
392
+ Custom ranking:
393
+ - Add business metrics (popularity, rating, date)
394
+ - Use desc() for descending, asc() for ascending
395
+
396
+ ### Code_example
397
+
398
+ // scripts/configure-index.ts
399
+ import algoliasearch from 'algoliasearch';
400
+
401
+ const adminClient = algoliasearch(
402
+ process.env.ALGOLIA_APP_ID!,
403
+ process.env.ALGOLIA_ADMIN_KEY!
404
+ );
405
+
406
+ const index = adminClient.initIndex('products');
407
+
408
+ async function configureIndex() {
409
+ await index.setSettings({
410
+ // Searchable attributes in order of importance
411
+ searchableAttributes: [
412
+ 'name', // Most important
413
+ 'brand',
414
+ 'category',
415
+ 'description', // Least important
416
+ ],
417
+
418
+ // Attributes for faceting/filtering
419
+ attributesForFaceting: [
420
+ 'category',
421
+ 'brand',
422
+ 'filterOnly(inStock)', // Filter only, not displayed
423
+ 'searchable(tags)', // Searchable facet
424
+ ],
425
+
426
+ // Custom ranking (after text relevance)
427
+ customRanking: [
428
+ 'desc(popularity)', // Most popular first
429
+ 'desc(rating)', // Then by rating
430
+ 'desc(createdAt)', // Then by recency
431
+ ],
432
+
433
+ // Typo tolerance
434
+ typoTolerance: true,
435
+ minWordSizefor1Typo: 4,
436
+ minWordSizefor2Typos: 8,
437
+
438
+ // Query settings
439
+ queryLanguages: ['en'],
440
+ removeStopWords: ['en'],
441
+
442
+ // Highlighting
443
+ attributesToHighlight: ['name', 'description'],
444
+ highlightPreTag: '<mark>',
445
+ highlightPostTag: '</mark>',
446
+
447
+ // Pagination
448
+ hitsPerPage: 20,
449
+ paginationLimitedTo: 1000,
450
+
451
+ // Distinct (deduplication)
452
+ attributeForDistinct: 'productFamily',
453
+ distinct: true,
454
+ });
455
+
456
+ // Add synonyms
457
+ await index.saveSynonyms([
458
+ {
459
+ objectID: 'phone-mobile',
460
+ type: 'synonym',
461
+ synonyms: ['phone', 'mobile', 'cell', 'smartphone'],
462
+ },
463
+ {
464
+ objectID: 'laptop-notebook',
465
+ type: 'oneWaySynonym',
466
+ input: 'laptop',
467
+ synonyms: ['notebook', 'portable computer'],
468
+ },
469
+ ]);
470
+
471
+ // Add rules (query-based customization)
472
+ await index.saveRules([
473
+ {
474
+ objectID: 'boost-sale-items',
475
+ condition: {
476
+ anchoring: 'contains',
477
+ pattern: 'sale',
478
+ },
479
+ consequence: {
480
+ params: {
481
+ filters: 'onSale:true',
482
+ optionalFilters: ['featured:true'],
483
+ },
484
+ },
485
+ },
486
+ ]);
487
+
488
+ console.log('Index configured successfully');
489
+ }
490
+
491
+ configureIndex();
492
+
493
+ ### Anti_patterns
494
+
495
+ - Pattern: Searching all attributes equally | Why: Reduces relevance, matches in descriptions rank same as titles | Fix: Order searchableAttributes by importance
496
+ - Pattern: No custom ranking | Why: Relies only on text matching, ignores business value | Fix: Add popularity, rating, or recency to customRanking
497
+ - Pattern: Indexing raw dates as strings | Why: Can't sort by date correctly | Fix: Use timestamps (getTime()) for date sorting
498
+
499
+ ### References
500
+
501
+ - https://www.algolia.com/doc/guides/managing-results/relevance-overview
502
+ - https://www.algolia.com/doc/guides/managing-results/must-do/custom-ranking
503
+
504
+ ### Faceted Search and Filtering
505
+
506
+ Implement faceted navigation with refinement lists, range sliders,
507
+ and hierarchical menus.
508
+
509
+ Widget types:
510
+ - RefinementList: Multi-select checkboxes
511
+ - Menu: Single-select list
512
+ - HierarchicalMenu: Nested categories
513
+ - RangeInput/RangeSlider: Numeric ranges
514
+ - ToggleRefinement: Boolean filters
515
+
516
+ ### Code_example
517
+
518
+ 'use client';
519
+ import {
520
+ InstantSearch,
521
+ SearchBox,
522
+ Hits,
523
+ RefinementList,
524
+ HierarchicalMenu,
525
+ RangeInput,
526
+ ToggleRefinement,
527
+ ClearRefinements,
528
+ CurrentRefinements,
529
+ Stats,
530
+ SortBy,
531
+ } from 'react-instantsearch';
532
+ import { searchClient, INDEX_NAME } from '@/lib/algolia';
533
+
534
+ export function ProductSearch() {
535
+ return (
536
+ <InstantSearch searchClient={searchClient} indexName={INDEX_NAME}>
537
+ <div className="flex gap-8">
538
+ {/* Filters Sidebar */}
539
+ <aside className="w-64 space-y-6">
540
+ <ClearRefinements />
541
+ <CurrentRefinements />
542
+
543
+ {/* Category hierarchy */}
544
+ <div>
545
+ <h3 className="font-semibold mb-2">Categories</h3>
546
+ <HierarchicalMenu
547
+ attributes={[
548
+ 'categories.lvl0',
549
+ 'categories.lvl1',
550
+ 'categories.lvl2',
551
+ ]}
552
+ limit={10}
553
+ showMore
554
+ />
555
+ </div>
556
+
557
+ {/* Brand filter */}
558
+ <div>
559
+ <h3 className="font-semibold mb-2">Brand</h3>
560
+ <RefinementList
561
+ attribute="brand"
562
+ searchable
563
+ searchablePlaceholder="Search brands..."
564
+ showMore
565
+ limit={5}
566
+ showMoreLimit={20}
567
+ />
568
+ </div>
55
569
 
56
- | Issue | Severity | Solution |
57
- |-------|----------|----------|
58
- | Issue | critical | See docs |
59
- | Issue | high | See docs |
60
- | Issue | medium | See docs |
61
- | Issue | medium | See docs |
62
- | Issue | medium | See docs |
63
- | Issue | medium | See docs |
64
- | Issue | medium | See docs |
65
- | Issue | medium | See docs |
570
+ {/* Price range */}
571
+ <div>
572
+ <h3 className="font-semibold mb-2">Price</h3>
573
+ <RangeInput
574
+ attribute="price"
575
+ precision={0}
576
+ classNames={{
577
+ input: 'w-20 px-2 py-1 border rounded',
578
+ }}
579
+ />
580
+ </div>
581
+
582
+ {/* In stock toggle */}
583
+ <ToggleRefinement
584
+ attribute="inStock"
585
+ label="In Stock Only"
586
+ on={true}
587
+ />
588
+
589
+ {/* Rating filter */}
590
+ <div>
591
+ <h3 className="font-semibold mb-2">Rating</h3>
592
+ <RefinementList
593
+ attribute="rating"
594
+ transformItems={(items) =>
595
+ items.map((item) => ({
596
+ ...item,
597
+ label: '★'.repeat(Number(item.label)),
598
+ }))
599
+ }
600
+ />
601
+ </div>
602
+ </aside>
603
+
604
+ {/* Results */}
605
+ <main className="flex-1">
606
+ <div className="flex justify-between items-center mb-4">
607
+ <SearchBox placeholder="Search products..." />
608
+ <SortBy
609
+ items={[
610
+ { label: 'Relevance', value: 'products' },
611
+ { label: 'Price (Low to High)', value: 'products_price_asc' },
612
+ { label: 'Price (High to Low)', value: 'products_price_desc' },
613
+ { label: 'Rating', value: 'products_rating_desc' },
614
+ ]}
615
+ />
616
+ </div>
617
+ <Stats />
618
+ <Hits hitComponent={ProductHit} />
619
+ </main>
620
+ </div>
621
+ </InstantSearch>
622
+ );
623
+ }
624
+
625
+ // For sorting, create replica indices
626
+ // products_price_asc: customRanking: ['asc(price)']
627
+ // products_price_desc: customRanking: ['desc(price)']
628
+ // products_rating_desc: customRanking: ['desc(rating)']
629
+
630
+ ### Anti_patterns
631
+
632
+ - Pattern: Faceting on non-faceted attributes | Why: Must declare attributesForFaceting in settings | Fix: Add attributes to attributesForFaceting array
633
+ - 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
634
+
635
+ ### References
636
+
637
+ - https://www.algolia.com/doc/guides/managing-results/refine-results/faceting
638
+ - https://www.algolia.com/doc/api-reference/widgets/refinement-list/react
639
+
640
+ ### Query Suggestions and Autocomplete
641
+
642
+ Implement autocomplete with query suggestions and instant results.
643
+
644
+ Uses @algolia/autocomplete-js for standalone autocomplete or
645
+ integrate with InstantSearch using SearchBox.
646
+
647
+ Query Suggestions require a separate index generated by Algolia.
648
+
649
+ ### Code_example
650
+
651
+ // Standalone Autocomplete
652
+ // components/Autocomplete.tsx
653
+ 'use client';
654
+ import { autocomplete, getAlgoliaResults } from '@algolia/autocomplete-js';
655
+ import algoliasearch from 'algoliasearch/lite';
656
+ import { useEffect, useRef } from 'react';
657
+ import '@algolia/autocomplete-theme-classic';
658
+
659
+ const searchClient = algoliasearch(
660
+ process.env.NEXT_PUBLIC_ALGOLIA_APP_ID!,
661
+ process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_KEY!
662
+ );
663
+
664
+ export function Autocomplete() {
665
+ const containerRef = useRef<HTMLDivElement>(null);
666
+
667
+ useEffect(() => {
668
+ if (!containerRef.current) return;
669
+
670
+ const search = autocomplete({
671
+ container: containerRef.current,
672
+ placeholder: 'Search for products',
673
+ openOnFocus: true,
674
+ getSources({ query }) {
675
+ if (!query) return [];
676
+
677
+ return [
678
+ // Query suggestions
679
+ {
680
+ sourceId: 'suggestions',
681
+ getItems() {
682
+ return getAlgoliaResults({
683
+ searchClient,
684
+ queries: [
685
+ {
686
+ indexName: 'products_query_suggestions',
687
+ query,
688
+ params: { hitsPerPage: 5 },
689
+ },
690
+ ],
691
+ });
692
+ },
693
+ templates: {
694
+ header() {
695
+ return 'Suggestions';
696
+ },
697
+ item({ item, html }) {
698
+ return html`<span>${item.query}</span>`;
699
+ },
700
+ },
701
+ },
702
+ // Instant results
703
+ {
704
+ sourceId: 'products',
705
+ getItems() {
706
+ return getAlgoliaResults({
707
+ searchClient,
708
+ queries: [
709
+ {
710
+ indexName: 'products',
711
+ query,
712
+ params: { hitsPerPage: 8 },
713
+ },
714
+ ],
715
+ });
716
+ },
717
+ templates: {
718
+ header() {
719
+ return 'Products';
720
+ },
721
+ item({ item, html }) {
722
+ return html`
723
+ <a href="/products/${item.objectID}">
724
+ <img src="${item.image}" alt="${item.name}" />
725
+ <span>${item.name}</span>
726
+ <span>$${item.price}</span>
727
+ </a>
728
+ `;
729
+ },
730
+ },
731
+ onSelect({ item, setQuery, refresh }) {
732
+ // Navigate on selection
733
+ window.location.href = `/products/${item.objectID}`;
734
+ },
735
+ },
736
+ ];
737
+ },
738
+ });
739
+
740
+ return () => search.destroy();
741
+ }, []);
742
+
743
+ return <div ref={containerRef} />;
744
+ }
745
+
746
+ // Combined with InstantSearch
747
+ import { connectSearchBox } from 'react-instantsearch';
748
+ import { autocomplete } from '@algolia/autocomplete-js';
749
+
750
+ // Or use built-in Autocomplete widget
751
+ import { Autocomplete as AlgoliaAutocomplete } from 'react-instantsearch';
752
+
753
+ export function SearchWithAutocomplete() {
754
+ return (
755
+ <InstantSearch searchClient={searchClient} indexName="products">
756
+ <AlgoliaAutocomplete
757
+ placeholder="Search products..."
758
+ detachedMediaQuery="(max-width: 768px)"
759
+ />
760
+ <Hits hitComponent={ProductHit} />
761
+ </InstantSearch>
762
+ );
763
+ }
764
+
765
+ ### Anti_patterns
766
+
767
+ - Pattern: Creating autocomplete without debouncing | Why: Every keystroke triggers search, wastes operations | Fix: Algolia autocomplete handles debouncing automatically
768
+ - Pattern: Not using Query Suggestions index | Why: Missing search analytics for popular queries | Fix: Enable Query Suggestions in Algolia dashboard
769
+
770
+ ### References
771
+
772
+ - https://www.algolia.com/doc/ui-libraries/autocomplete/introduction/what-is-autocomplete
773
+ - https://www.algolia.com/doc/guides/building-search-ui/ui-and-ux-patterns/query-suggestions/how-to/optimizing-query-suggestions-relevance/js
774
+
775
+ ## Sharp Edges
776
+
777
+ ### Admin API Key in Frontend Code
778
+
779
+ Severity: CRITICAL
780
+
781
+ ### Indexing Rate Limits and Throttling
782
+
783
+ Severity: HIGH
784
+
785
+ ### Record Size and Index Limits
786
+
787
+ Severity: MEDIUM
788
+
789
+ ### PII in Index Names Visible in Network
790
+
791
+ Severity: MEDIUM
792
+
793
+ ### Searchable Attributes Order Affects Relevance
794
+
795
+ Severity: MEDIUM
796
+
797
+ ### Full Reindex Consumes All Operations
798
+
799
+ Severity: MEDIUM
800
+
801
+ ### Every Keystroke Counts as Search Operation
802
+
803
+ Severity: MEDIUM
804
+
805
+ ### SSR Hydration Mismatch with InstantSearch
806
+
807
+ Severity: MEDIUM
808
+
809
+ ### Replica Indices for Sorting Multiply Storage
810
+
811
+ Severity: LOW
812
+
813
+ ### Faceting Requires attributesForFaceting Declaration
814
+
815
+ Severity: MEDIUM
816
+
817
+ ## Validation Checks
818
+
819
+ ### Admin API Key in Client Code
820
+
821
+ Severity: ERROR
822
+
823
+ Admin API key must never be exposed to client-side code
824
+
825
+ Message: Admin API key exposed to client. Use search-only key.
826
+
827
+ ### Hardcoded Algolia API Key
828
+
829
+ Severity: ERROR
830
+
831
+ API keys should use environment variables
832
+
833
+ Message: Hardcoded Algolia credentials. Use environment variables.
834
+
835
+ ### Search Key Used for Indexing
836
+
837
+ Severity: ERROR
838
+
839
+ Indexing operations require admin key, not search key
840
+
841
+ Message: Search key used for indexing. Use admin key for write operations.
842
+
843
+ ### Single Record Indexing in Loop
844
+
845
+ Severity: WARNING
846
+
847
+ Batch records together for efficient indexing
848
+
849
+ Message: Single record indexing in loop. Use saveObjects for batch indexing.
850
+
851
+ ### Using deleteBy for Deletion
852
+
853
+ Severity: WARNING
854
+
855
+ deleteBy is expensive and rate-limited
856
+
857
+ Message: deleteBy is expensive. Prefer deleteObjects with specific IDs.
858
+
859
+ ### Frequent Full Reindex
860
+
861
+ Severity: WARNING
862
+
863
+ Full reindex wastes operations on unchanged data
864
+
865
+ Message: Frequent full reindex. Consider incremental sync for unchanged data.
866
+
867
+ ### Full Client Instead of Lite
868
+
869
+ Severity: INFO
870
+
871
+ Use lite client for smaller bundle in frontend
872
+
873
+ Message: Full Algolia client imported. Use algoliasearch/lite for frontend.
874
+
875
+ ### Regular InstantSearch in Next.js
876
+
877
+ Severity: WARNING
878
+
879
+ Use react-instantsearch-nextjs for SSR support
880
+
881
+ Message: Using regular InstantSearch. Use InstantSearchNext for Next.js SSR.
882
+
883
+ ### Missing Searchable Attributes Configuration
884
+
885
+ Severity: WARNING
886
+
887
+ Configure searchableAttributes for better relevance
888
+
889
+ Message: No searchableAttributes configured. Set attribute priority for relevance.
890
+
891
+ ### Missing Custom Ranking
892
+
893
+ Severity: INFO
894
+
895
+ Custom ranking improves business relevance
896
+
897
+ Message: No customRanking configured. Add business metrics (popularity, rating).
898
+
899
+ ## Collaboration
900
+
901
+ ### Delegation Triggers
902
+
903
+ - user needs e-commerce checkout -> stripe-integration (Product search leading to purchase)
904
+ - user needs search analytics -> segment-cdp (Track search queries and results)
905
+ - user needs user authentication -> clerk-auth (Secured API keys per user)
906
+ - user needs database setup -> postgres-wizard (Source data for indexing)
907
+ - user needs serverless deployment -> aws-serverless (Lambda for indexing jobs)
66
908
 
67
909
  ## When to Use
68
- This skill is applicable to execute the workflow or actions described in the overview.
910
+
911
+ - User mentions or implies: adding search to
912
+ - User mentions or implies: algolia
913
+ - User mentions or implies: instantsearch
914
+ - User mentions or implies: search api
915
+ - User mentions or implies: search functionality
916
+ - User mentions or implies: typeahead
917
+ - User mentions or implies: autocomplete search
918
+ - User mentions or implies: faceted search
919
+ - User mentions or implies: search index
920
+ - User mentions or implies: search as you type