Stabilize Algolia search feedback#1069
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5cf7f8b. Configure here.
| > | ||
| {errorMessage} | ||
| </p> | ||
| ); |
There was a problem hiding this comment.
Stale error during debounced typing
Medium Severity
After a search fails, the SearchResults component can display a stale error message. The typedQuery updates immediately, but the debounced search() call (which would update the search status) is delayed. This causes the error message to persist for a new query, even as the user continues typing.
Reviewed by Cursor Bugbot for commit 5cf7f8b. Configure here.
| } | ||
| if (!query.trim()) { | ||
| search(query); | ||
| return null; |
There was a problem hiding this comment.
Whitespace skips clear search
Low Severity
scheduleSearch treats whitespace-only input as empty via trim, but still calls search with the raw spaces instead of clearing to an empty query. SearchResults meanwhile shows the empty prompt for that input, so InstantSearch can run a whitespace query the UI never reflects.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 5cf7f8b. Configure here.
Debounce queries and suppress stale result renders so the search dialog reports loading, empty, and error states without flicker or literal highlight tags. Co-authored-by: Cursor <cursoragent@cursor.com>
Only render a search failure when it belongs to the query currently shown in the dialog. Co-authored-by: Cursor <cursoragent@cursor.com>
Track the dispatched query independently so current failures stay visible while stale failures disappear for new input. Co-authored-by: Cursor <cursoragent@cursor.com>
3579173 to
3154310
Compare
teallarson
left a comment
There was a problem hiding this comment.
Code review — 4 findings across correctness, efficiency, and reuse. Ranked highest-severity first.
| const normalizedQuery = query.trim(); | ||
| return ( | ||
| normalizedQuery.length > 0 && | ||
| status === "idle" && |
There was a problem hiding this comment.
Bug: status === "stalled" hides valid cached results
React InstantSearch sets status to "stalled" when a request exceeds stalledSearchDelay (default 200ms). At that point results still holds the previous matching response — results.query equals typedQuery and results.nbHits > 0 — but searchResultsAreCurrent returns false because it gates on status === "idle" strictly. The caller falls through to the "Searching…" placeholder, replacing valid results with a loading message for the entire stall duration.
Suggested fix:
return (
normalizedQuery.length > 0 &&
(status === "idle" || status === "stalled") &&
resultsQuery.trim() === normalizedQuery
);| query: string; | ||
| search: (nextQuery: string) => void; | ||
| setTypedQuery: (nextQuery: string) => void; | ||
| setDispatchedQuery?: (nextQuery: string) => void; |
There was a problem hiding this comment.
Misleading optional on a consequence-bearing parameter
setDispatchedQuery is typed as optional (?), so external callers of the exported scheduleSearch can legally omit it. When omitted, dispatchedQuery stays at "", making searchErrorIsCurrent always return false — Algolia errors are silently swallowed and the UI shows an indefinite loading state.
Since this parameter is mandatory for correct error display, consider removing the ? or adding a JSDoc warning.
| } | ||
| if (!query.trim()) { | ||
| setDispatchedQuery?.(query); | ||
| search(query); |
There was a problem hiding this comment.
Unnecessary Algolia request on every input clear
The empty-query fast path calls search(query) (i.e. search("")) immediately without debouncing. SearchResults then discards the response via its !query.trim() early return. This fires a real Algolia API round-trip on every clear, consuming operations quota for a response that is always thrown away.
| delayMs?: number; | ||
| }; | ||
|
|
||
| export function scheduleSearch({ |
There was a problem hiding this comment.
scheduleSearch duplicates useDebounce from @uidotdev/usehooks (already a dep)
@uidotdev/usehooks v2.4.1 is already in the dep tree (used in app/en/resources/integrations/components/use-toolkit-filters.ts). scheduleSearch + the useRef<setTimeout> block in SearchContent re-implements the same debounce logic, creating two divergent patterns to maintain.
Replacing with the existing hook would remove the manual timer management, eliminate the exported scheduleSearch, and keep both debounced flows in the codebase consistent.


Summary
Test plan
pnpm exec vitest run tests/algolia-search.test.tsNote
Low Risk
Docs-only UI and client-side Algolia behavior; no auth, data, or API surface changes beyond search UX.
Overview
Improves the docs Algolia modal so typing, loading, and results stay in sync and highlights render correctly.
Debounced queries (150ms via
SearchBoxqueryHookandscheduleSearch) cut redundant Algolia requests while typing; clearing the input still searches immediately.Unified result UI replaces separate empty/no-results components with
SearchResults, which shows a start-typing prompt, Searching… untilsearchResultsAreCurrentmatches the typed query and status is idle, zero-hit messaging, or hits—and an alert on search error (catchError+getSearchErrorMessage).Configure is centralized in
ALGOLIA_SEARCH_CONFIG, including React InstantSearch highlight tags (__ais-highlight__) so snippets don’t show raw markers. Modal markup moves intoSearchContentwith timer cleanup on unmount.Adds Vitest coverage for debounce, highlight config, stale-result guard, and error copy.
Reviewed by Cursor Bugbot for commit 5cf7f8b. Bugbot is set up for automated code reviews on this repo. Configure here.