--- title: Build search interfaces (Shopify Hydrogen) slug: p27e1343 canonical_url: https://docs.coveo.com/en/p27e1343/ collection: coveo-for-commerce source_format: adoc --- # Build search interfaces (Shopify Hydrogen) Building a [search interface](https://docs.coveo.com/en/2741/) with [Coveo Headless](https://docs.coveo.com/en/lcdf0493/) and Shopify Hydrogen involves three main components: a search box, a search page, and a search provider. The Coveo Headless React package provides two types of search box controllers: the [`SearchBox`](https://docs.coveo.com/en/headless-react/latest/reference/types/SSR_Commerce.index.SearchBox.html) and the [`StandaloneSearchBox`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.StandaloneSearchBox.html). It also exposes functions to create a search [provider](https://docs.coveo.com/en/p3ae0283#headless-specific-providers), which you can use to manage the search page and results. Complete examples are available in the [Barca Sports Hydrogen repository](https://github.com/coveo-labs/barca-sports-hydrogen/blob/main/app/routes/(%24locale).search.tsx). ## Prerequisite Make sure that you understand how to [build commerce search pages](https://docs.coveo.com/en/o4ue0200/). ## Creating a search box To create a search box, use the `SearchBox` and `StandaloneSearchBox` controllers: * `SearchBox`: Use this controller to create a search box component in your search interface, submit queries, and display [query suggestions](https://docs.coveo.com/en/1015/). This controller is used on the search page itself. * `StandaloneSearchBox`: Use this controller to create a standalone search box that redirects to your search page. This controller is used on every page of your app except the search page. ### Define the controllers Create a component that uses the [`SearchBox`](https://docs.coveo.com/en/headless-react/latest/reference/types/SSR_Commerce.index.SearchBox.html) controller for the search page and a lightweight standalone search box component that uses the [`StandaloneSearchBox`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.StandaloneSearchBox.html) controller to redirect to your search page. This standalone search box component should be included on every page of your app except the search page. When [defining your commerce engine](https://docs.coveo.com/en/p3ae0283#initializing-the-engine), define both search box controllers. ```ts // lib/commerce-engine-config.ts import { defineSearchBox, defineStandaloneSearchBox, // ... } from '@coveo/headless-react/ssr-commerce'; export default { // ... controllers: { searchBox: defineSearchBox(), <1> standaloneSearchBox: defineStandaloneSearchBox({ <2> options: {redirectionUrl: '/search'}, }), // ... }, } satisfies CommerceEngineDefinitionOptions; ``` <1> Define the `SearchBox` controller via the [`defineSearchBox`](https://docs.coveo.com/en/headless-react/latest/reference/functions/SSR_Commerce.index.defineSearchBox.html) function. <2> Define the `StandaloneSearchBox` controller via the [`defineStandaloneSearchBox`](https://docs.coveo.com/en/headless-react/latest/reference/functions/SSR_Commerce.index.defineStandaloneSearchBox.html) function and specify the redirection URL. When the user submits a query in this search box, the [`state.redirectTo`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.StandaloneSearchBoxState.html#redirectto) is updated to `/search`. This update can be detected by subscribing to the controller state and then used to redirect the user to the search page. ### Using the controllers The following example shows how to create a component using the `StandaloneSearchBox` controller. The `SearchBox` component isn't included in this article, but it follows a similar implementation without the redirection logic. ```tsx import {useEffect, useRef} from 'react'; import {useStandaloneSearchBox} from '~/lib/commerce-engine.ts'; import {useNavigate} from '@remix-run/react'; export function StandaloneSearchBox() { const searchBox = useStandaloneSearchBox(); <1> const inputRef = useRef(null); const navigate = useNavigate(); useEffect(() => { <2> inputRef.current?.focus(); }, []); useEffect(() => { <3> if (searchBox.state.redirectTo === '/search') { navigate( `${searchBox.state.redirectTo}?q=${encodeURIComponent( searchBox.state.value, )}`, ); } }, [searchBox.state.redirectTo, searchBox.state.value]); const handleSuggestionClick = (suggestion: string) => { searchBox.methods?.updateText(suggestion); inputRef.current!.value = suggestion; searchBox.methods?.showSuggestions(); }; return (
ref={inputRef} aria-label="Search" placeholder="Search" onChange={(e) => searchBox.methods?.updateText(e.target.value)} onFocus={() => searchBox.methods?.showSuggestions()} /> {searchBox.state.suggestions.length > 0 && ( <5>
{searchBox.state.suggestions.map((suggestion) => (
)}
); } ``` <1> Use the `useStandaloneSearchBox` hook to access the `StandaloneSearchBox` controller. <2> Focus the input field when the component mounts. <3> Redirect the user to the search page when the `redirectTo` state is updated to `/search`. Navigate to the search page with the query text. <4> Render the input field and bind the necessary event handlers. As the user types, update the query text and show suggestions using the [`updateText`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.StandaloneSearchBox.html#updatetext-1) and [`showSuggestions`](https://docs.coveo.com/en/headless-react/latest/reference/interfaces/SSR_Commerce.index.StandaloneSearchBox.html#showsuggestions-1) methods. <5> Loop through the suggestions and render them. <6> Handle the suggestion click event. Update the search box text, show the suggestions, and set the input value. ## Displaying the search page with the search provider Once the search box is set up, you must display the search results on the search page, using your search provider, which manages [facets](https://docs.coveo.com/en/198/), sorting, and pagination to ensure a seamless user experience. For your search page route, add the following code to render the search page with the search provider. ```tsx import {type LoaderFunctionArgs} from '@shopify/remix-oxygen'; import {useLoaderData, type MetaFunction} from '@remix-run/react'; import { fetchStaticState, searchEngineDefinition, type SearchStaticState, } from '~/lib/commerce-engine.ts'; import { ClientSideNavigatorContextProvider, ServerSideNavigatorContextProvider, } from '~/lib/navigator.provider'; import {SearchProvider} from '~/components/Search/Context'; import ParameterManager from '~/components/ParameterManager'; import {buildParameterSerializer} from '@coveo/headless-react/ssr-commerce'; import {useEffect, useState} from 'react'; import {Facets} from '~/components/Search/Facets'; import {Sorts} from '~/components/Search/Sorts'; import {Pagination} from '~/components/Search/Pagination'; import {ProductList} from '~/components/Search/ProductList'; export const meta: MetaFunction = () => { return [{title: `Coveo | Search`}]; }; export type SearchLoader = typeof loader; export async function loader({request, context}: LoaderFunctionArgs) { <1> const url = new URL(request.url); const {deserialize} = buildParameterSerializer(); const parameters = deserialize(url.searchParams); <2> const q = url.searchParams.get('q') || ''; searchEngineDefinition.setNavigatorContextProvider( <3> () => new ServerSideNavigatorContextProvider(request), ); const staticState = await fetchStaticState({ <4> context, k: 'searchEngineDefinition', parameters, url: `https://shop.barca.group`, request, }); return {staticState, q, url}; } export default function SearchPage() { const {staticState, q, url} = useLoaderData(); const [currentUrl, setCurrentUrl] = useState(url); useEffect(() => { setCurrentUrl(window.location.href); }, []); return ( navigatorContext={new ClientSideNavigatorContextProvider()} staticState={staticState as SearchStaticState} > <6> <7> <8> <9> ); } ``` <1> Use the [`loader`](https://remix.run/docs/en/main/route/loader) function to run server-side code. <2> [Fetch the deserialized search parameters from the URL.](https://docs.coveo.com/en/p23d7042#use-your-parametermanager-component) <3> [Set the navigator context provider to the server-side context provider.](https://docs.coveo.com/en/p3ae0283#navigation-context-providers) <4> [Fetch the static state for the search page.](https://docs.coveo.com/en/p3ae0283#fetch-static-headless-state) <5> [Wrap the search page with the `SearchProvider` component.](https://docs.coveo.com/en/p3ae0283#headless-specific-providers) <6> [Use the `ParameterManager` component to manage search parameters.](https://docs.coveo.com/en/p23d7042/) <7> Render a `SearchBox` component to display the search box. This component will be similar to the [standalone search box component](#using-the-controllers), but it will use the `SearchBox` controller instead of the `StandaloneSearchBox` controller and not include the redirection logic. <8> [Render the `ProductList` component to display search results.](https://docs.coveo.com/en/p24a0093/) <9> Render the `Facets` component to display facets. Similarly, render the `Sorts` and `Pagination` components to display sorting and pagination controls. For more details, see [Leveraging facets, sorting, and pagination.](https://docs.coveo.com/en/p25e0438/) A complete example of how to build a search page is available in the [Barca Sports Hydrogen repository](https://github.com/coveo-labs/barca-sports-hydrogen/blob/main/app/routes/(%24locale).search.tsx). Within the sample project, you can find additional components that were omitted in this article, such as the [`BreadcrumbManager`](https://docs.coveo.com/en/headless-react/latest/reference/types/SSR_Commerce.index.BreadcrumbManager.html) which displays a summary of the currently active facet values.