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.
-
RequestResponse
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
WidgetDataType
enumerator,useRecommendation
query hook, andwidget
function from the Discover JS SDK for React. -
Import statements for
ArticleCardStyled
headless primitive UI component from the UI components CLI package. -
The
ArticleModel
for the entity typearticle
used in the request query. -
RecommendationProps
with the necessary properties for the widget. -
InitialState
-
-
RequestResponse
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:
title
is used in the output, andproductsToDisplay
is passed in the query.Property
Type
Description
title
string
String used as a title for the widget.
itemsToDisplay
integer
Maximum number of items in the response and display in the widget.
Default: 4
-
RequestResponse
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
useRecommendation
query hook. This generic response of the query hook includes three objects:action
,state
andqueryResult
. In this example, when a product name is clicked, we trigger theonItemClick
action from the response. We also extract thearticles
array fromdata
in the response. -
RequestResponse
const loading = !isLoading && !isFetching && articles.length > 0;
The widget is displayed when the boolean ready evaluated using the query hook response,
isLoading
,isFetching
andproducts
. -
RequestResponse
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
articles
array and populates the headlessArticleCardStyled
primitive 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. -
RequestResponse
const RecommendationBasicWidget = widget(RecommendationBasicHorizontalStyledComponent, WidgetDataType.RECOMMENDATION,"content");
widget
is 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 thewidget
function. -
RequestResponse
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;