Product Search
A storefront search field with live suggestions, categories and product results. Handles typos, slow or failing searches, recent searches and keyboard-only use.
Product Search is the search field in a store header. On a desktop it opens a dropdown under the field, and on a phone a full-screen sheet with the keyboard up. Before anything is typed it shows recent searches, trending searches and a row of popular products.
As the shopper types, it shows up to eight query suggestions with the predicted part in bold, category matches such as “linen in Bedding”, a few products with a thumbnail and price, and a final “Search for” row. Arrowing onto a suggestion copies it into the field so it can be edited, Escape puts back what was typed, and Tab completes the suggestion.
onSearch(query, { signal }) is called after a short debounce. Older requests are aborted, answers are cached so backspacing is instant, and the previous results stay on screen while the next ones load. The spinner only appears when a search takes longer than 150ms. A failed search shows its error message with a Try again row, and a search with no results offers “Did you mean” and popular categories.
Recent searches are kept in localStorage under recentStorageKey, or controlled with recent and onRecentChange. SearchResultsHeader is the matching heading for a results page, “Results for “vase” (4)”, with a Did you mean line. The fuzzy helpers in search.ts, fuzzySearch, completeQuery and correctQuery, power the demo and work for small catalogs on their own.
HextaUI Pro holen
Eine einmalige Zahlung schaltet jeden Block frei, auch künftige. Preise ansehen.
Die Pro-Registry zu components.json hinzufügen
components.json Token hinzufügen
Erstelle auf deiner Kontoseite einen Token und trage ihn in
.env.localalsHEXTAUI_PRO_TOKENein.Den Block hinzufügen
pnpm dlx shadcn@latest add @hextaui-pro/product-search
Shopify predictive search
Call the Storefront API’s predictiveSearch with the signal ProductSearch passes in, and map queries, collections and products to SearchResults. Prices are already Money, so they go straight through.
Algolia
One multi-query request fetches query suggestions and products together. Algolia’s typo tolerance and facet counts fill the suggestions, the “in Bedding” rows and the result count.
Next.js search page
Send Enter to /search?q= with the router, and seed the field from the URL so the results page echoes the query. Products and categories open their own routes.
Aufbau
Die Teile, die du zusammensetzt, von außen nach innen.
| Part | Beschreibung |
|---|---|
ProductSearch | The field, the ⌘K hint and the dropdown or full-screen sheet. |
SearchResultsHeader | The results page heading with the count and a spelling suggestion. |
fuzzySearch, completeQuery, correctQuery | Typo-tolerant matching, query completion and spelling fixes for small catalogs. |
splitCompletion, addRecent, removeRecent | Split a suggestion into typed and predicted parts, and edit a recent searches list. |
ProductSearch
Also takes div props.
| Prop | Typ | Standard |
|---|---|---|
onSearchFetches results. Abort when signal fires. A thrown error’s message is shown in the dropdown. | (query, { signal }) => Promise<SearchResults> | – |
onSubmitCalled when the shopper searches for text or picks a suggestion, recent or trending search. | (query: string) => void | – |
onSelectProductCalled when a product is picked. Without it, the product’s href opens. | (product, query) => void | – |
onSelectCollectionCalled when a category is picked. Without it, the category’s href opens. | (collection, query) => void | – |
valueThe query, when controlled. | string | – |
defaultValueThe starting query. | string | "" |
onValueChangeCalled as the shopper types. | (query: string) => void | – |
openWhether the dropdown or sheet is open, when controlled. | boolean | – |
onOpenChangeCalled when it opens or closes. | (open: boolean) => void | – |
recentStorageKeyKeep recent searches in localStorage under this key. | string | – |
recentRecent searches, when controlled. | string[] | – |
defaultRecentRecent searches to show when nothing is stored. | string[] | [] |
onRecentChangeCalled when a search is added, removed or cleared. | (recent: string[]) => void | – |
maxRecentHow many recent searches to keep. | number | 5 |
trendingTrending searches for the empty state. | string[] | [] |
popularProductsProducts for the empty state. Shows four, or three on a phone. | SearchProduct[] | [] |
popularCollectionsCategories to offer when nothing matches. | SearchCollection[] | [] |
maxSuggestionsQuery suggestions to show. | number | 8, or 5 on a phone |
maxProductsProduct results to show. | number | 4, or 3 on a phone |
debounceMilliseconds to wait after typing before searching. | number | 120 |
shortcutOpen with ⌘K, Ctrl+K and /. | boolean | true |
alignAlign the dropdown with the field. | "start" | "center" | "end" | "start" |
localeLocale for prices and counts. | Intl.LocalesArgument | "en-US" |
labelsWords for headings, rows, hints and announcements. | Partial<ProductSearchLabels> | – |
| Prop | Typ | Standard |
|---|---|---|
suggestionsQuery completions, best first. Only suggest queries that have results. | string[] | – |
collectionsCategories that match, shown as “query in Bedding”. | { id; title; href?; count? }[] | – |
productsProduct results. price is Money or { min, max }. | { id; title; href?; image?; price?; compareAt?; subtitle? }[] | – |
totalThe full result count for the live announcement. Defaults to products.length. | number | – |
correctionThe corrected query when you matched a typo. Shown as “Showing results for”, or as “Did you mean” when nothing matched. | string | – |
SearchResultsHeader
Also takes div props.
| Prop | Typ | Standard |
|---|---|---|
queryThe query shown in the heading. | string | – |
countThe result count. Hidden while null. | number | null | – |
suggestionA spelling suggestion shown as “Did you mean”. | string | null | – |
correctedFromThe original query when the results are for a correction, shown as “Search instead for”. | string | null | – |
onSuggestionSelectCalled when either link is clicked. | (query: string) => void | – |
headingLevelThe heading level. | 1 | 2 | 3 | 1 |
| Taste | Aktion |
|---|---|
| Meta+KControl+K | Focus the field and open search. |
| / | Open search, unless you are typing in another field. |
| ArrowDownArrowUp | Move through every row. Suggestions are copied into the field as you go. |
| Enter | Open the highlighted row, or search for what was typed. |
| Tab | Complete the highlighted or top suggestion and keep typing. |
| Shift+Delete | Remove the highlighted recent search. |
| Escape | Put back what was typed, then close, then clear the field. |
- The field is a combobox with a listbox of grouped options. aria-activedescendant follows the highlighted row, so focus never leaves the field.
- Result counts such as “6 results” are announced once each search settles, never per keystroke. A failed search is announced as an alert.
- Closing the phone sheet returns focus to the search button. The field uses 16px text on touch screens so iOS doesn’t zoom.
- Highlighted rows get an outline in forced colors mode, and the spinner, fades and arrow nudge are reduced or removed with reduced motion.
Gebaut mit
Die kostenlosen HextaUI-Komponenten, aus denen Product Search besteht. Jede lässt sich einzeln installieren.
Code
6 Dateien, hinzugefügt zu components/blocks/product-search.