Widget templates
Sitecore Search widgets are dynamic components added to a page or an email message to deliver specific experiences. The primary purpose of a widget is to display content from Search. Unlike content personalization widgets, which only display previously configured content, merchandising widgets can modify data requests based on context or user interactions.
The SearchJS SDK for React package includes the following widget templates that can be instantiated only with a rfk_id. At runtime, the JS SDK requests data for the widgets and supplies them with data required for configuration and display.
Search results, preview search and recommendation widgets are not included in the JS SDK because they need functional React components to be associated with them.
The JS SDK only provides query hooks and high order functions that you can use to create your component or template.
Anatomy of a basic recommendation widget template
In order to understand how widgets are written in UI components CLI package, the code for a styled recommendation widget has been broken down into smaller blocks. Each block is annotated to describe the various elements of a widget template.
-
import ( React ) from 'react'; import { WidgetDataType, useRecommendation, widget, RecommendationInitialState } from '@sitecore-search/react'; import { Presence } from '@sitecore-search/ui'; import { ArticleCardStyled, GridStyled, LoaderAnimation, LoaderContainer, RecommendationContainer, TitleStyled, } from './styled'; type ArticleModel = { id: string; name: string; author?: string; url?: string; image_url?: string; source_id?: string; }; type RecommendationProps = { title?: string; itemsToDisplay?: number; }; type InitialState = RecommendationInitialState<'itemsPerPage'>;This block incudes the following:
-
Import statements for
WidgetDataTypeenumerator,useRecommendationquery hook, andwidgetfunction from the Discover JS SDK for React. -
Import statements for
ArticleCardStyledheadless primitive UI component from the UI components CLI package. -
The
ArticleModelfor the entity typearticleused in the request query. -
RecommendationPropswith the necessary properties for the widget. -
InitialState
-
-
export const RecommendationBasic = ({ title = '', itemsToDisplay = 6 }) => {This is the declaration of the React UI component for a basic recommendation widget. The following table describes the arguments:
titleis used in the output, andproductsToDisplayis passed in the query.Property
Type
Description
titlestring
String used as a title for the widget.
itemsToDisplayinteger
Maximum number of items in the response and display in the widget.
Default: 4
-
export const RecommendationBasicHorizontalStyledComponent = ({ title = '', itemsToDisplay = 6 }: RecommendationProps) => { const { widgetRef, actions: { onItemClick }, queryResult: { isLoading, isFetching, data: { content: articles = [] } = {} }, } = useRecommendation<ArticleModel, InitialState>({ state: { itemsPerPage: itemsToDisplay, }, });To create a recommendation widget, you must use the
useRecommendationquery hook. This generic response of the query hook includes three objects:action,stateandqueryResult. In this example, when a product name is clicked, we trigger theonItemClickaction from the response. We also extract thearticlesarray fromdatain the response. -
const loading = !isLoading && !isFetching && articles.length > 0;The widget is displayed when the boolean ready evaluated using the query hook response,
isLoading,isFetchingandproducts. -
return ( <RecommendationContainer> <Presence present={loading}> <LoaderContainer> <LoaderAnimation aria-busy={loading} aria-hidden={!loading} focusable="false" role="progressbar" viewBox="0 0 20 20" > <path d="M7.229 1.173a9.25 9.25 0 1 0 11.655 11.412 1.25 1.25 0 1 0-2.4-.698 6.75 6.75 0 1 1-8.506-8.329 1.25 1.25 0 1 0-.75-2.385z" /> </LoaderAnimation> </LoaderContainer> </Presence> {loading && ( <> {title && <TitleStyled>{title}</TitleStyled>} <GridStyled ref={widgetRef}> {articles.map((item, index) => ( <ArticleCardStyled.Root key={item.id}> <ArticleCardStyled.ImageWrapper> <ArticleCardStyled.Image src={item?.image_url || DEFAULT_IMG_URL} /> </ArticleCardStyled.ImageWrapper> <ArticleCardStyled.Content> <ArticleCardStyled.Link href={item.url} onClick={(event) => { event.preventDefault(); onItemClick({ id: item.id, index, sourceId: item.source_id }); }} > <ArticleCardStyled.Title>{item.name}</ArticleCardStyled.Title> <ArticleCardStyled.Subtitle>{item.author}</ArticleCardStyled.Subtitle> </ArticleCardStyled.Link> </ArticleCardStyled.Content> </ArticleCardStyled.Root> ))} </GridStyled> </> )} </RecommendationContainer> );The HTML returned by this React component describes the display of this simple component. It loops through the
articlesarray and populates the headlessArticleCardStyledprimitive UI component. You can only use the attributes in the display that are marked to be included in the response. Unmarked attributes can result in errors. -
const RecommendationBasicWidget = widget(RecommendationBasicHorizontalStyledComponent, WidgetDataType.RECOMMENDATION,"content");widgetis a high order function that converts the React UI component into a widget component that can be added as a descendant ofWidgetsProvider. We also need to provide the widget and the entity types for the results. The query hook used in the React UI component must match the widget type used with thewidgetfunction. -
export default RecommendationBasicWidget;Export statement of the widget component.
The following is the entire code for the basic recommendation widget. For search results and preview search widgets, use their respective query hooks.
import React from 'react'; import { WidgetDataType, useRecommendation, widget, RecommendationInitialState } from '@sitecore-search/react'; import { Presence } from '@sitecore-search/ui'; import { ArticleCardStyled, GridStyled, LoaderAnimation, LoaderContainer, RecommendationContainer, TitleStyled, } from './styled'; type ArticleModel = { id: string; name: string; author?: string; url?: string; image_url?: string; source_id?: string; }; type RecommendationProps = { title?: string; itemsToDisplay?: number; }; type InitialState = RecommendationInitialState<'itemsPerPage'>; export const RecommendationBasicHorizontalStyledComponent = ({ title = '', itemsToDisplay = 6 }: RecommendationProps) => { const { widgetRef, actions: { onItemClick }, queryResult: { isLoading, isFetching, data: { content: articles = [] } = {} }, } = useRecommendation<ArticleModel, InitialState>({ state: { itemsPerPage: itemsToDisplay, }, }); const loading = !isLoading && !isFetching && articles.length > 0; return ( <RecommendationContainer> <Presence present={loading}> <LoaderContainer> <LoaderAnimation aria-busy={loading} aria-hidden={!loading} focusable="false" role="progressbar" viewBox="0 0 20 20" > <path d="M7.229 1.173a9.25 9.25 0 1 0 11.655 11.412 1.25 1.25 0 1 0-2.4-.698 6.75 6.75 0 1 1-8.506-8.329 1.25 1.25 0 1 0-.75-2.385z" /> </LoaderAnimation> </LoaderContainer> </Presence> {loading && ( <> {title && <TitleStyled>{title}</TitleStyled>} <GridStyled ref={widgetRef}> {articles.map((item, index) => ( <ArticleCardStyled.Root key={item.id}> <ArticleCardStyled.ImageWrapper> <ArticleCardStyled.Image src={item?.image_url || DEFAULT_IMG_URL} /> </ArticleCardStyled.ImageWrapper> <ArticleCardStyled.Content> <ArticleCardStyled.Link href={item.url} onClick={(event) => { event.preventDefault(); onItemClick({ id: item.id, index, sourceId: item.source_id }); }} > <ArticleCardStyled.Title>{item.name}</ArticleCardStyled.Title> <ArticleCardStyled.Subtitle>{item.author}</ArticleCardStyled.Subtitle> </ArticleCardStyled.Link> </ArticleCardStyled.Content> </ArticleCardStyled.Root> ))} </GridStyled> </> )} </RecommendationContainer> ); }; const RecommendationStyledWidget = widget(RecommendationBasicHorizontalStyledComponent, WidgetDataType.RECOMMENDATION, "content"); export default RecommendationStyledWidget;