Insights

Setting Up Coveo Headless Standalone Search Box

Purpose of a Standalone Search Box

When you utilize Coveo and build custom interfaces one thing that will stand out is that of the Search Box. In particular, the fact there are two ways of implementing a search box. A normal search box that's included within a search page and that of a standalone version.

The main difference is that the Standalone version is fully independent and doesn't interact directly with things like results and facets. Its sole purpose is to take in a search query, present potential Query Suggestions (if applicable), and the redirect the query to the search result page.

In a Headless environment, this is achieved using local storage to store the query-related information before it's redirected to the search results. The Standalone Search Box effectively never executes a search query.

Let's Build It

The first thing to do is to set up the initialization of the Standalone Search Box. There aren't many options, but you can find the full list here: StandaloneSearchBoxOptions.

We won't be going into how to display Query Suggestions this time around but hope to in a future article.

Setup Search Box Initialization

The initialization of the standalone search box is relatively clean and simple. We use the HeadlessEngineContext we created previously. Once done, we pass the controller we obtained from buildStandaloneSearchBox to our renderer function. You could very well combine these two if so desired.

const StandaloneSearchBox = (props: any) => {
  const options: StandaloneSearchBoxOptions = {
    numberOfSuggestions: props.numOfPages,
    redirectionUrl: props.searchPage,
  };
  const engine = useContext(HeadlessEngineContext) as unknown as SearchEngine;
  const controller = buildStandaloneSearchBox(engine, { options });
  return (
    <SearchBoxRenderer
      engine={engine}
      controller={controller}
      searchPage={props.searchPage as string}
    />
  );
};

Setup Renderer Function

As part of the renderer, we've added two buttons that assist with both the submission as well as the clearing of the query. For those we've used react-feather to display the icons.

Within the if statement for redirectTo, if true, we store the query value and analytics into local storage to be read by the result page.


  interface StandaloneSearchBoxProps {
    engine: SearchEngine;
    controller: HeadlessSearchBox;
    searchPage: string;
  }

  const SearchBoxRenderer: FunctionComponent<StandaloneSearchBoxProps> = (props) => {
    const { controller } = props;
    const [state, setState] = useState(controller.state);

    // Ensure we detect users who hit Enter key.
    const isEnterKey = (e: React.KeyboardEvent<HTMLInputElement>) => e.key === 'Enter';
  
    // Need function when a query is Submitted.
    const submitQuery = (): void => {
      if (!state) {
        return;
      }

      const { redirectTo, value, analytics } = state;
  
      // If there is a redirectTo value, this indicates we want to send the user to the result page.
      if (redirectTo) {
        const data = { value, analytics };
        localStorage.setItem('coveo_standalone_search_box_data', JSON.stringify(data));
        window.location.href = state.redirectTo;
        return;
      }
    };
  
    // What to do when user clicks to clear query.
    const clearQuery = (): void => {
      if (!state) {
        return;
      }
      controller.updateText('');
    };
  
    submitQuery();
  
    useEffect(() => controller.subscribe(() => setState(controller.state)), []);
  
    return (
      <div>
        <input
          type={'text'}
          value={state.value}
          onChange={(e) => {
            controller.updateText(e.target.value);
            showQuerySummary(e.target.value);
          }}
          onKeyDown={(e) => isEnterKey(e) && controller.submit()}
          className={props.searchBoxStyling.inputField}
        />
        <div
          id={'search-icon'}
          onClick={() => submitQuery()}
          tabIndex={0}
          onKeyPress={(e) => (e.key === 'Enter' ? submitQuery() : null)}
        >
          <Search />
        </div>
        <div
          id={'cancel-search-icon'}
          onClick={() => clearQuery()}
          tabIndex={0}
          onKeyPress={(e) => (e.key === 'Enter' ? clearQuery() : null)}
        >
          <X />
        </div>
      </div>
    );
  };

Within the returned DOM we have an input box, a search button, and a clear button. Each has appropriate onChange, onClick, or onKeyPress events that trigger the appropriate responses.

As I mentioned above, this is a very simple Standalone Search Box. You can of course expand upon it to display results from the Query Suggestion model if it's being utilized.

👋 Hey Sitecore Enthusiasts!

Sign up to our bi-weekly newsletter for a bite-sized curation of valuable insight from the Sitecore community.

What’s in it for you?

  • Stay up-to-date with the latest Sitecore news
  • New to Sitecore? Learn tips and tricks to help you navigate this powerful tool
  • Sitecore pro? Expand your skill set and discover troubleshooting tips
  • Browse open careers and opportunities
  • Get a chance to be featured in upcoming editions
  • Learn our secret handshake
  • And more!
Sitecore Snack a newsletter by Fishtank Consulting
 

Meet David Austin

Development Team Lead | Sitecore Technology MVP x 3

📷🕹️👪

David is a decorated Development Team Lead with Sitecore Technology MVP and Coveo MVP awards, as well as Sitecore CDP & Personalize Certified. He's worked in IT for 25 years; everything ranging from Developer to Business Analyst to Group Lead helping manage everything from Intranet and Internet sites to facility management and application support. David is a dedicated family man who loves to spend time with his girls. He's also an avid photographer and loves to explore new places.

Connect with David