Yext has developed a series of tools designed to make UI development with the Search API easier. This includes a series of components that can be added to any React application.
Under the hood, the components are powered by Search Headless React — a library that manages data returned by the Search API in a global SearchHeadless instance. This is implemented with Redux, "a predictable state container for JavaScript apps." There's a lot of boilerplate code that comes with setting up Redux for calling APIs based on user-triggered events and modifying the state accordingly.
Search Headless React removes the complexity of having to write all the boilerplate for Redux + Search API. It also comes with a series of custom hooks for rendering Search state data in the UI and changing the Search state based on events triggered by the components.
This article introduces Search Headless React, the hooks that come with it, and how the Search UI React components use headless.
Search Headless React: State and Hooks
Search Headless React
If you were to develop a Redux state management solution for the Search API, you would need to:
- Design the shape of your Redux store to accommodate the search state of your application.
- Write API client functions for calling the Search APIs — including search queries, autocomplete, and filter search.
- Create action functions that call the API client functions before dispatching actions.
- Create reducer functions responsible for modifying and returning a new copy of the Redux store.
Search Headless React takes care of all of this setup for you. It also offers its own provider component and hooks that are conceptually similar to useSelector and useDispatch.
SearchHeadlessProvider and provideHeadless
To use the Search Headless React hooks, components need to be children of a <SearchHeadlessProvider />. The provider requires a prop called searcher, which is an instance of the SearchHeadless state. To instantiate a new SearchHeadless instance, use the provideHeadless hook.
provideHeadless requires a HeadlessConfig argument with the following mandatory fields:
-
apiKey— the unique Search API key for your Yext account -
experienceKey— the unique key for a specific Search experience -
locale— locale (language) of the Search experience
Under the hood, these props authenticate the API calls made by Search Headless to the Search API. Any nested component within the <SearchHeadlessProvider /> can use the Search Headless hooks.
import {
SearchHeadlessProvider,
provideHeadless,
} from "@yext/search-headless-react";
// ...other imports
const searcher = provideHeadless({
apiKey: "YOUR_API_KEY",
experienceKey: "YOUR_EXPERIENCE_KEY",
locale: "en",
});
ReactDOM.render(
<React.StrictMode>
<SearchHeadlessProvider searcher={searcher}>
<App />
</SearchHeadlessProvider>
</React.StrictMode>,
document.getElementById("root")
);
useSearchState
Similar to React Redux's useSelector hook, useSearchState extracts data from the search store for use by the component. The Search store is passed as an argument and a specified part of the store is returned. Just like useSelector, useSearchState will run when the component renders or when a Search action is dispatched.
import { useSearchState } from "@yext/search-headless-react";
export default function MostRecentSearch() {
const mostRecentSearch = useSearchState(
(state) => state.query.mostRecentSearch
);
return <div>Showing results for {mostRecentSearch}</div>;
}
You can find all the Search properties exposed by the state object here.
useSearchActions
useSearchActions allows components to dispatch actions like searching, autocomplete, and applying facets. Just like useDispatch, you can use functions from this hook to trigger search actions from events triggered by your components.
The example below is an input element that calls setQuery to update the search query in the store on each keystroke, and executeUniversalQuery when the Enter key is pressed.
import { useSearchActions } from "@yext/search-headless-react";
import { ChangeEvent, KeyboardEvent, useCallback } from "react";
function SearchBar() {
const search = useSearchActions();
const handleTyping = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
search.setQuery(e.target.value);
},
[search]
);
const handleKeyDown = useCallback(
(evt: KeyboardEvent<HTMLInputElement>) => {
if (evt.key === "Enter") {
search.executeUniversalQuery();
}
},
[search]
);
return <input onChange={handleTyping} onKeyDown={handleKeyDown} />;
}
You can find all the Search action functions here.
Developing Components with Headless React
Search Headless React has everything you need to create your own custom UI search components.
Building things like search bars with autocomplete is complex — you need to consider:
- Calling the
executeAutocompleteSearchaction on every keystroke - Re-rendering the autocomplete dropdown each time the autocomplete state changes
- Event handling for clicks on the autocomplete results, search icon, and X icon
You can build this yourself, but Yext recommends importing the <SearchBar /> component from @yext/search-ui-react, and the <VerticalResults /> component to render vertical search results.
See the source code for <VerticalResults /> and <SearchBar /> to see how they use the Search Headless React hooks.