Display instant products (Shopify Hydrogen)

This is for:

Developer

Instant products enhance the search experience by displaying relevant products in real time as users type in the search box.

Instant results with Headless in Shopify | Coveo for Commerce

To display instant products, you can use the InstantProducts controller.

To implement, first, define the controller:

// lib/commerce-engine-config.ts

import {
  defineInstantProducts,
  // ...
} from '@coveo/headless-react/ssr-commerce';

export default {
  // ...
  controllers: {
    instantProducts: defineInstantProducts(),
    // ...
  },
} satisfies CommerceEngineDefinitionOptions;

Next, incorporate the controller into your existing search box component to seamlessly integrate instant product features into the search experience.

import {useEffect, useRef} from 'react';
import {useStandaloneSearchBox, useInstantProducts} from '~/lib/commerce-engine.ts';
import {useNavigate} from '@remix-run/react';

function useUpdateInstantProducts( 1
  searchBox: ReturnType<typeof useStandaloneSearchBox>,
  instantProducts: ReturnType<typeof useInstantProducts>,
) {
  useEffect(() => {
    if (searchBox.state.suggestions[0]) {
      instantProducts.methods?.updateQuery(
        searchBox.state.suggestions[0]?.rawValue,
      );
    }
  }, [searchBox.state.suggestions, instantProducts.methods]);
}

export function StandaloneSearchBox() {
  const searchBox = useStandaloneSearchBox();
  const inputRef = useRef<HTMLInputElement>(null);
  const navigate = useNavigate();
  const instantProducts = useInstantProducts(); 2
  useUpdateInstantProducts(searchBox, instantProducts); 3

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  useEffect(() => {
    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 (
    <div>
      <input
        ref={inputRef}
        aria-label="Search"
        placeholder="Search"
        onChange={(e) => searchBox.methods?.updateText(e.target.value)}
        onFocus={() => searchBox.methods?.showSuggestions()}
      />
      <button onClick={searchBox.methods?.submit}>Search</button>

      {searchBox.state.suggestions.length > 0 && (
        <div>
          {searchBox.state.suggestions.map((suggestion) => (
            <button
              key={suggestion.rawValue}
              onClick={() => handleSuggestionClick(suggestion.rawValue)}
              dangerouslySetInnerHTML={{__html: suggestion.highlightedValue}}
            />
          ))}
        </div>
      )}

      {searchBox.state.suggestions.length > 0 && instantProducts.state.products.length > 0 && ( 4
          <div>
            {instantProducts.state.products.map((product) => (
              <div key={product.ec_product_id}>
                <h2>{product.ec_name}</h2>
              </div>
            ))}
          </div>
        )}
    </div>
  );
}
1 Create a update the state of the InstantProducts controller via the updateQuery method whenever the state.suggestions value changes on the search box. This occurs when the user types in the search box.
2 Use the useInstantProducts hook to access the InstantProducts controller.
3 Use the useUpdateInstantProducts hook defined above, to sync the state of the InstantProducts controller with the search box.
4 Display the instant products by iterating through the instantProducts.state.products array.