Request context for Sitecore Search React SDK


When using Sitecore Search all requests need several information as context for the search request.

  • Widget Id (rfkid) is required, in the backend each “widget” is configured. Here business rules can be applied such as boosting, blacklisting etc. It is also used for analytics so you can get much more detailed and focused data about search usage.

  • User Context is required for personalization. It is actually managed automatically from cookies. You might want to annotate a used identifier during login such as an id or even email, Facebook id or similar. When making any server side requests you can use a ser context by providing the user.uuid property.

  • Locale context is required, when managing pages with Sitecore Search it can be enough to provide a page.uri. We are mostly using a CMS for the website, often Sitecore XM/XP/DXP, so - instead of creating each uri in Sitecore search - we provide the locale.country and locale.language properties. Both are required, as localization in Sitecore Search is based on the full locale with both language and country.

Setting Locale context

We need to provide country and language on each request - and they need to match the ones created in Sitecore Search…

We usually also have full locale on the website as there will often arise business requirements eg. products available on certain markets, multiple languages on a specific market, language differences across markets, etc. However, it also happens that we have applications with only a market dimension. It is then necessary to provide a country when interacting with Sitecore Search, so in the underlying system/codebase we actually match a language to a country, even though it is not exposed to the users of the application.

Search React SDK

When using the Search React SDK you can easy and fast build search experiences and there are plenty of customization options. Things like translations, suggestions blocks, and highlighting needs to be added anyway and it is simple to add context here also.

Eg. when using the useSearchResults hook, the code in the search result list can look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  const state = useSearchResults<ContentModel, InitialState>({
    query: (q) => {
      const req = q
        .getRequest()
        .setSearchQueryHighlightFragmentSize(500)
        .setSearchQueryHighlightFields(['name', 'title', 'description'])
        .setSearchQueryHighlightPreTag('<highlight>')
        .setSearchQueryHighlightPostTag('</highlight>');

      req.addSearchSuggestion({
        name: 'keyword_typo',
        max: 3,
        keyphrase_fallback: true,
      });

      const ctx = q.getContext();
      ctx.setLocaleCountry(country);
      ctx.setLocaleLanguage(language);

      return q;
    },
    state: {
      sortType: defaultSortType,
      page: defaultPage,
      itemsPerPage: defaultItemsPerPage,
      keyphrase: startQuery,
    },
  });

When using Sitecore JSS (either Sitecore XM Cloud or Sitecore DXP) for creating headless solutions, the country and language can be fetched from the context with this snippet:

1
2
3
4
const { sitecoreContext } = useSitecoreContext();
const [language, country] = sitecoreContext.language
  .split("-")
  .map((x) => x.toLowerCase());

Language and country is always lowercase in Sitecore Search, while in the Sitecore CMS context the country code is (usually) uppercase and matching the underlying CultureInfo so we ensure it is lowercase. If there are routes without country, you will of course need to handle it so it will be a bit more complex than the code above.

The subquery problem

When using PreviewSearch the widget itself can actually create additional requests when hovering suggestions. Those queries are creating internally in the SDK by cloning the main query with a useWidgetSubQuery hook. However, that cloning doesn’t take the locale context into account, so the requests will fail as bad requests.

Luckily, there actually is a solution for this, I was just not aware of it. Instead of specifying locale on each and every we can specify this globally by using PageController.

1
2
PageController.getContext().setLocaleLanguage(language);
PageController.getContext().setLocaleCountry(country);

So, instead we have in the search-provider.ts:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { WidgetsProvider, PageController } from '@sitecore-search/react';

export function SearchProvider({ children }: Readonly<ChildrenProps>): JSX.Element {
  const { sitecoreContext } = useSitecoreContext();
  const [language, country] = sitecoreContext.language
  .split("-")
  .map((x) => x.toLowerCase());
  PageController.getContext().setLocaleLanguage(language);
  PageController.getContext().setLocaleCountry(country);

  return (
    <WidgetsProvider
      env={searchEnv}
      customerKey={searchCustomerKey}
      apiKey={searchApiKey}
      serviceHost={searchApiHost}
      trackConsent={cookiesAccepted}
    >
      {children}
    </WidgetsProvider>
  );
}

The PageController has actually been available in the React SDK from the beginning (at least back to version 1.2.0 I checked) so this approach should be available on all implementations.

The initial request is created by the PageController, now including the locale context, and when the subQuery hook clone procedure updates with properties from the parent query, the locale context is kept.

I got the help to use this approach with the help of Sitecore Support, so a great thank you.